Additional invitation stuff

This commit is contained in:
Adrian Wozniak 2020-04-21 19:35:39 +02:00
parent 219b4d3640
commit 80fb8f5466
7 changed files with 384 additions and 528 deletions

View File

@ -1,62 +1,101 @@
use actix::Addr; use actix::AsyncContext;
use actix_web::web::Data; use futures::executor::block_on;
use jirs_data::WsMsg; use jirs_data::{Token, WsMsg};
use crate::db::tokens::CreateBindToken; use crate::db::authorize_user::AuthorizeUser;
use crate::db::tokens::{CreateBindToken, FindBindToken};
use crate::db::users::FindUser; use crate::db::users::FindUser;
use crate::db::DbExecutor;
use crate::mail::welcome::Welcome; use crate::mail::welcome::Welcome;
use crate::mail::MailExecutor; use crate::ws::{WebSocketActor, WsHandler, WsResult};
use crate::ws::WsResult;
pub async fn authenticate( pub struct Authenticate {
db: &Data<Addr<DbExecutor>>, pub name: String,
mail: &Data<Addr<MailExecutor>>, pub email: String,
name: String, }
email: String,
) -> WsResult { impl WsHandler<Authenticate> for WebSocketActor {
// TODO check attempt number, allow only 5 times per day fn handle_msg(&mut self, msg: Authenticate, _ctx: &mut Self::Context) -> WsResult {
let user = match db.send(FindUser { name, email }).await { let Authenticate { name, email } = msg;
Ok(Ok(user)) => user, // TODO check attempt number, allow only 5 times per day
Ok(Err(e)) => { let user = match block_on(self.db.send(FindUser { name, email })) {
error!("{:?}", e); Ok(Ok(user)) => user,
return Ok(None);
}
Err(e) => {
error!("{:?}", e);
return Ok(None);
}
};
let token = match db.send(CreateBindToken { user_id: user.id }).await {
Ok(Ok(token)) => token,
Ok(Err(e)) => {
error!("{:?}", e);
return Ok(None);
}
Err(e) => {
error!("{:?}", e);
return Ok(None);
}
};
if let Some(bind_token) = token.bind_token.as_ref().cloned() {
match mail
.send(Welcome {
bind_token,
email: user.email.clone(),
})
.await
{
Ok(Ok(_)) => (),
Ok(Err(e)) => { Ok(Err(e)) => {
error!("{}", e); error!("{:?}", e);
return Ok(None); return Ok(None);
} }
Err(e) => { Err(e) => {
error!("{}", e); error!("{:?}", e);
return Ok(None); return Ok(None);
} }
};
let token = match block_on(self.db.send(CreateBindToken { user_id: user.id })) {
Ok(Ok(token)) => token,
Ok(Err(e)) => {
error!("{:?}", e);
return Ok(None);
}
Err(e) => {
error!("{:?}", e);
return Ok(None);
}
};
if let Some(bind_token) = token.bind_token.as_ref().cloned() {
match block_on(self.mail.send(Welcome {
bind_token,
email: user.email.clone(),
})) {
Ok(Ok(_)) => (),
Ok(Err(e)) => {
error!("{}", e);
return Ok(None);
}
Err(e) => {
error!("{}", e);
return Ok(None);
}
}
} }
Ok(Some(WsMsg::AuthenticateSuccess))
}
}
pub struct CheckAuthToken {
pub token: uuid::Uuid,
}
impl WsHandler<CheckAuthToken> for WebSocketActor {
fn handle_msg(&mut self, msg: CheckAuthToken, ctx: &mut Self::Context) -> WsResult {
let user: jirs_data::User = match block_on(self.db.send(AuthorizeUser {
access_token: msg.token,
})) {
Ok(Ok(u)) => u.into(),
Ok(Err(_)) => {
return Ok(Some(WsMsg::AuthorizeLoaded(Err(
"Invalid auth token".to_string()
))))
}
_ => return Ok(Some(WsMsg::AuthorizeExpired)),
};
self.current_user = Some(user.clone());
block_on(self.join_channel(ctx.address().recipient()));
Ok(Some(WsMsg::AuthorizeLoaded(Ok(user))))
}
}
pub struct CheckBindToken {
pub bind_token: uuid::Uuid,
}
impl WsHandler<CheckBindToken> for WebSocketActor {
fn handle_msg(&mut self, msg: CheckBindToken, _ctx: &mut Self::Context) -> WsResult {
let token: Token = match block_on(self.db.send(FindBindToken {
token: msg.bind_token,
})) {
Ok(Ok(token)) => token,
Ok(Err(_)) => return Ok(Some(WsMsg::BindTokenBad)),
_ => return Ok(None),
};
Ok(Some(WsMsg::BindTokenOk(token.access_token)))
} }
Ok(Some(WsMsg::AuthenticateSuccess))
} }

View File

@ -1,98 +1,98 @@
use actix::Addr; use actix::Addr;
use actix_web::web::Data; use actix_web::web::Data;
use futures::executor::block_on;
use jirs_data::{CommentId, CreateCommentPayload, IssueId, UpdateCommentPayload, WsMsg}; use jirs_data::{CommentId, CreateCommentPayload, IssueId, UpdateCommentPayload, WsMsg};
use crate::db::comments::LoadIssueComments;
use crate::db::DbExecutor; use crate::db::DbExecutor;
use crate::ws::{current_user, WsResult}; use crate::ws::{current_user, WebSocketActor, WsHandler, WsResult};
pub async fn load_issues( pub struct LoadIssueComments {
db: &Data<Addr<DbExecutor>>, pub issue_id: IssueId,
user: &Option<jirs_data::User>,
issue_id: IssueId,
) -> WsResult {
current_user(user)?;
let comments = match db.send(LoadIssueComments { issue_id }).await {
Ok(Ok(comments)) => comments.into_iter().map(|c| c.into()).collect(),
_ => return Ok(None),
};
Ok(Some(WsMsg::IssueCommentsLoaded(comments)))
} }
pub async fn create_comment( impl WsHandler<LoadIssueComments> for WebSocketActor {
db: &Data<Addr<DbExecutor>>, fn handle_msg(&mut self, msg: LoadIssueComments, _ctx: Self::Context) -> WsResult {
user: &Option<jirs_data::User>, self.require_user()?;
mut payload: CreateCommentPayload,
) -> WsResult {
use crate::db::comments::CreateComment;
let user_id = current_user(user)?.id; let comments = match block_on(self.db.send(crate::db::comments::LoadIssueComments {
if payload.user_id.is_none() { issue_id: msg.issue_id,
payload.user_id = Some(user_id); })) {
Ok(Ok(comments)) => comments.into_iter().map(|c| c.into()).collect(),
_ => return Ok(None),
};
Ok(Some(WsMsg::IssueCommentsLoaded(comments)))
} }
let issue_id = payload.issue_id; }
match db
.send(CreateComment { impl WsHandler<CreateCommentPayload> for WebSocketActor {
fn handle_msg(&mut self, mut msg: CreateCommentPayload, _ctx: Self::Context) -> WsResult {
use crate::db::comments::CreateComment;
let user_id = self.require_user()?.id;
if msg.user_id.is_none() {
msg.user_id = Some(user_id);
}
let issue_id = msg.issue_id;
match block_on(self.db.send(CreateComment {
user_id, user_id,
issue_id, issue_id,
body: payload.body, body: msg.body,
}) })) {
.await Ok(Ok(_)) => (),
{ _ => return Ok(None),
Ok(Ok(_)) => (), };
_ => return Ok(None), self.handle_msg(LoadIssueComments { issue_id })
}; }
load_issues(db, user, issue_id).await
} }
pub async fn update_comment( impl WsHandler<UpdateCommentPayload> for WebSocketActor {
db: &Data<Addr<DbExecutor>>, fn handle_msg(&mut self, msg: UpdateCommentPayload, _ctx: Self::Context) -> WsResult {
user: &Option<jirs_data::User>, use crate::db::comments::UpdateComment;
payload: UpdateCommentPayload,
) -> WsResult {
use crate::db::comments::UpdateComment;
info!("{:?}", payload); info!("{:?}", msg);
let user_id = current_user(user)?.id; let user_id = self.require_user()?.id;
let UpdateCommentPayload { let UpdateCommentPayload {
id: comment_id, id: comment_id,
body, body,
} = payload; } = msg;
let issue_id = match db let issue_id = match block_on(self.db.send(UpdateComment {
.send(UpdateComment {
comment_id, comment_id,
user_id, user_id,
body, body,
}) })) {
.await Ok(Ok(comment)) => comment.issue_id,
{ _ => return Ok(None),
Ok(Ok(comment)) => comment.issue_id, };
_ => return Ok(None), if let Some(v) = self.handle_msg(LoadIssueComments { issue_id })? {
}; self.broadcast(&v);
load_issues(db, user, issue_id).await }
Ok(None)
}
} }
pub async fn delete_comment( pub struct DeleteComment {
db: &Data<Addr<DbExecutor>>, pub comment_id: CommentId,
user: &Option<jirs_data::User>, }
comment_id: CommentId,
) -> WsResult { impl WsHandler<DeleteComment> for WebSocketActor {
use crate::db::comments::DeleteComment; fn handle_msg(&mut self, msg: DeleteComment, _ctx: Self::Context) -> WsResult {
use crate::db::comments::DeleteComment;
let user_id = current_user(user)?.id;
let user_id = self.require_user()?.id;
let msg = DeleteComment {
comment_id, let m = DeleteComment {
user_id, comment_id: msg.comment_id,
}; user_id,
match db.send(msg).await { };
Ok(Ok(_)) => (), match block_on(self.db.send(m)) {
_ => return Ok(None), Ok(Ok(_)) => (),
}; _ => return Ok(None),
};
Ok(Some(WsMsg::CommentDeleted(comment_id)))
Ok(Some(WsMsg::CommentDeleted(msg.comment_id)))
}
} }

View File

@ -1,21 +1,14 @@
use actix::{Handler, Message};
use futures::executor::block_on; use futures::executor::block_on;
use jirs_data::{EmailString, InvitationId, UsernameString, WsMsg}; use jirs_data::{EmailString, InvitationId, UsernameString, WsMsg};
use crate::db::invitations; use crate::db::invitations;
use crate::ws::{WebSocketActor, WsResult}; use crate::ws::{WebSocketActor, WsHandler, WsResult};
pub struct ListInvitation; pub struct ListInvitation;
impl Message for ListInvitation { impl WsHandler<ListInvitation> for WebSocketActor {
type Result = WsResult; fn handle_msg(&mut self, _msg: ListInvitation, _ctx: &mut Self::Context) -> WsResult {
}
impl Handler<ListInvitation> for WebSocketActor {
type Result = WsResult;
fn handle(&mut self, _msg: ListInvitation, _ctx: &mut Self::Context) -> Self::Result {
let user_id = match self.current_user.as_ref().map(|u| u.id) { let user_id = match self.current_user.as_ref().map(|u| u.id) {
Some(id) => id, Some(id) => id,
_ => return Ok(None), _ => return Ok(None),
@ -33,14 +26,8 @@ pub struct CreateInvitation {
pub name: UsernameString, pub name: UsernameString,
} }
impl Message for CreateInvitation { impl WsHandler<CreateInvitation> for WebSocketActor {
type Result = WsResult; fn handle_msg(&mut self, msg: CreateInvitation, _ctx: &mut Self::Context) -> WsResult {
}
impl Handler<CreateInvitation> for WebSocketActor {
type Result = WsResult;
fn handle(&mut self, msg: CreateInvitation, _ctx: &mut Self::Context) -> Self::Result {
let (user_id, project_id) = match self.current_user.as_ref().map(|u| (u.id, u.project_id)) { let (user_id, project_id) = match self.current_user.as_ref().map(|u| (u.id, u.project_id)) {
Some(id) => id, Some(id) => id,
_ => return Ok(None), _ => return Ok(None),
@ -63,14 +50,8 @@ pub struct DeleteInvitation {
pub id: InvitationId, pub id: InvitationId,
} }
impl Message for DeleteInvitation { impl WsHandler<DeleteInvitation> for WebSocketActor {
type Result = WsResult; fn handle_msg(&mut self, msg: DeleteInvitation, _ctx: &mut Self::Context) -> WsResult {
}
impl Handler<DeleteInvitation> for WebSocketActor {
type Result = WsResult;
fn handle(&mut self, msg: DeleteInvitation, _ctx: &mut Self::Context) -> Self::Result {
self.require_user()?; self.require_user()?;
let DeleteInvitation { id } = msg; let DeleteInvitation { id } = msg;
let res = match block_on(self.db.send(invitations::DeleteInvitation { id })) { let res = match block_on(self.db.send(invitations::DeleteInvitation { id })) {
@ -85,14 +66,8 @@ pub struct RevokeInvitation {
pub id: InvitationId, pub id: InvitationId,
} }
impl Message for RevokeInvitation { impl WsHandler<RevokeInvitation> for WebSocketActor {
type Result = WsResult; fn handle_msg(&mut self, msg: RevokeInvitation, _ctx: &mut Self::Context) -> WsResult {
}
impl Handler<RevokeInvitation> for WebSocketActor {
type Result = WsResult;
fn handle(&mut self, msg: RevokeInvitation, _ctx: &mut Self::Context) -> Self::Result {
self.require_user()?; self.require_user()?;
let RevokeInvitation { id } = msg; let RevokeInvitation { id } = msg;
let res = match block_on(self.db.send(invitations::RevokeInvitation { id })) { let res = match block_on(self.db.send(invitations::RevokeInvitation { id })) {
@ -107,14 +82,8 @@ pub struct AcceptInvitation {
pub id: InvitationId, pub id: InvitationId,
} }
impl Message for AcceptInvitation { impl WsHandler<AcceptInvitation> for WebSocketActor {
type Result = WsResult; fn handle_msg(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> WsResult {
}
impl Handler<AcceptInvitation> for WebSocketActor {
type Result = WsResult;
fn handle(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> Self::Result {
self.require_user()?; self.require_user()?;
let AcceptInvitation { id } = msg; let AcceptInvitation { id } = msg;
let res = match block_on(self.db.send(invitations::AcceptInvitation { id })) { let res = match block_on(self.db.send(invitations::AcceptInvitation { id })) {

View File

@ -1,15 +1,12 @@
use std::collections::HashMap; use std::collections::HashMap;
use actix::*;
use actix_web::web::Data;
use futures::executor::block_on; use futures::executor::block_on;
use jirs_data::{IssueAssignee, IssueFieldId, PayloadVariant, WsMsg}; use jirs_data::{CreateIssuePayload, IssueAssignee, IssueFieldId, IssueId, PayloadVariant, WsMsg};
use crate::db::issue_assignees::LoadAssignees; use crate::db::issue_assignees::LoadAssignees;
use crate::db::issues::{LoadProjectIssues, UpdateIssue}; use crate::db::issues::{LoadProjectIssues, UpdateIssue};
use crate::db::DbExecutor; use crate::ws::{WebSocketActor, WsHandler, WsResult};
use crate::ws::{current_user, WebSocketActor, WsResult};
pub struct UpdateIssueHandler { pub struct UpdateIssueHandler {
pub id: i32, pub id: i32,
@ -17,18 +14,8 @@ pub struct UpdateIssueHandler {
pub payload: PayloadVariant, pub payload: PayloadVariant,
} }
impl Message for UpdateIssueHandler { impl WsHandler<UpdateIssueHandler> for WebSocketActor {
type Result = WsResult; fn handle_msg(&mut self, msg: UpdateIssueHandler, _ctx: &mut Self::Context) -> WsResult {
}
impl Actor for UpdateIssueHandler {
type Context = Context<Self>;
}
impl Handler<UpdateIssueHandler> for WebSocketActor {
type Result = WsResult;
fn handle(&mut self, msg: UpdateIssueHandler, _ctx: &mut Self::Context) -> Self::Result {
self.require_user()?; self.require_user()?;
let UpdateIssueHandler { let UpdateIssueHandler {
@ -90,145 +77,87 @@ impl Handler<UpdateIssueHandler> for WebSocketActor {
for assignee in assignees { for assignee in assignees {
issue.user_ids.push(assignee.user_id); issue.user_ids.push(assignee.user_id);
} }
self.broadcast(&WsMsg::IssueUpdated(issue));
Ok(Some(WsMsg::IssueUpdated(issue))) Ok(None)
} }
} }
pub async fn update_issue( impl WsHandler<CreateIssuePayload> for WebSocketActor {
db: &Data<Addr<DbExecutor>>, fn handle_msg(&mut self, msg: CreateIssuePayload, _ctx: &mut Self::Context) -> WsResult {
user: &Option<jirs_data::User>, self.require_user()?;
issue_id: i32, let msg = crate::db::issues::CreateIssue {
issue_field_id: IssueFieldId, title: msg.title,
payload: PayloadVariant, issue_type: msg.issue_type,
) -> WsResult { status: msg.status,
current_user(user)?; priority: msg.priority,
description: msg.description,
let mut msg = UpdateIssue::default(); description_text: msg.description_text,
msg.issue_id = issue_id; estimate: msg.estimate,
match (issue_field_id, payload) { time_spent: msg.time_spent,
(IssueFieldId::Type, PayloadVariant::IssueType(t)) => { time_remaining: msg.time_remaining,
msg.issue_type = Some(t); project_id: msg.project_id,
} reporter_id: msg.reporter_id,
(IssueFieldId::Title, PayloadVariant::String(s)) => { user_ids: msg.user_ids,
msg.title = Some(s);
}
(IssueFieldId::Description, PayloadVariant::String(s)) => {
msg.description = Some(s);
}
(IssueFieldId::Status, PayloadVariant::IssueStatus(s)) => {
msg.status = Some(s);
}
(IssueFieldId::ListPosition, PayloadVariant::I32(i)) => {
msg.list_position = Some(i);
}
(IssueFieldId::Assignees, PayloadVariant::VecI32(v)) => {
msg.user_ids = Some(v);
}
(IssueFieldId::Reporter, PayloadVariant::I32(i)) => {
msg.reporter_id = Some(i);
}
(IssueFieldId::Priority, PayloadVariant::IssuePriority(p)) => {
msg.priority = Some(p);
}
(IssueFieldId::Estimate, PayloadVariant::OptionI32(o)) => {
msg.estimate = o;
}
(IssueFieldId::TimeSpend, PayloadVariant::OptionI32(o)) => {
msg.time_spent = o;
}
(IssueFieldId::TimeRemaining, PayloadVariant::OptionI32(o)) => {
msg.time_remaining = o;
}
_ => (),
};
let mut issue: jirs_data::Issue = match db.send(msg).await {
Ok(Ok(issue)) => issue.into(),
_ => return Ok(None),
};
let assignees = match db.send(LoadAssignees { issue_id: issue.id }).await {
Ok(Ok(v)) => v,
_ => vec![],
};
for assignee in assignees {
issue.user_ids.push(assignee.user_id);
}
Ok(Some(WsMsg::IssueUpdated(issue)))
}
pub async fn add_issue(
db: &Data<Addr<DbExecutor>>,
user: &Option<jirs_data::User>,
payload: jirs_data::CreateIssuePayload,
) -> WsResult {
current_user(user)?;
let msg = crate::db::issues::CreateIssue {
title: payload.title,
issue_type: payload.issue_type,
status: payload.status,
priority: payload.priority,
description: payload.description,
description_text: payload.description_text,
estimate: payload.estimate,
time_spent: payload.time_spent,
time_remaining: payload.time_remaining,
project_id: payload.project_id,
reporter_id: payload.reporter_id,
user_ids: payload.user_ids,
};
let m = match db.send(msg).await {
Ok(Ok(issue)) => Some(WsMsg::IssueCreated(issue.into())),
_ => None,
};
Ok(m)
}
pub async fn delete_issue(
db: &Data<Addr<DbExecutor>>,
user: &Option<jirs_data::User>,
id: i32,
) -> WsResult {
current_user(user)?;
let m = match db
.send(crate::db::issues::DeleteIssue { issue_id: id })
.await
{
Ok(Ok(_)) => Some(WsMsg::IssueDeleted(id)),
_ => None,
};
Ok(m)
}
pub async fn load_issues(db: &Data<Addr<DbExecutor>>, user: &Option<jirs_data::User>) -> WsResult {
let project_id = current_user(user).map(|u| u.project_id)?;
let issues: Vec<jirs_data::Issue> = match db.send(LoadProjectIssues { project_id }).await {
Ok(Ok(v)) => v.into_iter().map(|i| i.into()).collect(),
_ => return Ok(None),
};
let mut issue_map = HashMap::new();
let mut queue = vec![];
for issue in issues.into_iter() {
let f = db.send(LoadAssignees { issue_id: issue.id });
queue.push(f);
issue_map.insert(issue.id, issue);
}
for f in queue {
if let Ok(Ok(assignees)) = f.await {
for assignee in assignees {
if let Some(issue) = issue_map.get_mut(&assignee.issue_id) {
issue.user_ids.push(assignee.user_id);
}
}
}; };
let m = match block_on(self.db.send(msg)) {
Ok(Ok(issue)) => Some(WsMsg::IssueCreated(issue.into())),
_ => None,
};
Ok(m)
}
}
pub struct DeleteIssue {
pub id: IssueId,
}
impl WsHandler<DeleteIssue> for WebSocketActor {
fn handle_msg(&mut self, msg: DeleteIssue, _ctx: &mut Self::Context) -> WsResult {
self.require_user()?;
let m = match block_on(
self.db
.send(crate::db::issues::DeleteIssue { issue_id: msg.id }),
) {
Ok(Ok(_)) => Some(WsMsg::IssueDeleted(msg.id)),
_ => None,
};
Ok(m)
}
}
pub struct LoadIssues;
impl WsHandler<LoadIssues> for WebSocketActor {
fn handle_msg(&mut self, _msg: LoadIssues, _ctx: &mut Self::Context) -> WsResult {
let project_id = self.require_user()?.project_id;
let issues: Vec<jirs_data::Issue> =
match block_on(self.db.send(LoadProjectIssues { project_id })) {
Ok(Ok(v)) => v.into_iter().map(|i| i.into()).collect(),
_ => return Ok(None),
};
let mut issue_map = HashMap::new();
let mut queue = vec![];
for issue in issues.into_iter() {
let f = self.db.send(LoadAssignees { issue_id: issue.id });
queue.push(f);
issue_map.insert(issue.id, issue);
}
for f in queue {
if let Ok(Ok(assignees)) = block_on(f) {
for assignee in assignees {
if let Some(issue) = issue_map.get_mut(&assignee.issue_id) {
issue.user_ids.push(assignee.user_id);
}
}
};
}
let mut issues = vec![];
for (_, issue) in issue_map.into_iter() {
issues.push(issue);
}
Ok(Some(WsMsg::ProjectIssuesLoaded(issues)))
} }
let mut issues = vec![];
for (_, issue) in issue_map.into_iter() {
issues.push(issue);
}
Ok(Some(WsMsg::ProjectIssuesLoaded(issues)))
} }

View File

@ -1,20 +1,18 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use actix::{ use actix::{Actor, ActorContext, Addr, Context, Handler, Message, Recipient, StreamHandler};
Actor, ActorContext, Addr, AsyncContext, Context, Handler, Message, Recipient, StreamHandler,
};
use actix_web::web::Data; use actix_web::web::Data;
use actix_web::{get, web, Error, HttpRequest, HttpResponse}; use actix_web::{get, web, Error, HttpRequest, HttpResponse};
use actix_web_actors::ws; use actix_web_actors::ws;
use futures::executor::block_on; use futures::executor::block_on;
use futures::SinkExt;
use jirs_data::{ProjectId, Token, UserId, WsMsg}; use jirs_data::{ProjectId, UserId, WsMsg};
use crate::db::authorize_user::AuthorizeUser;
use crate::db::tokens::FindBindToken;
use crate::db::DbExecutor; use crate::db::DbExecutor;
use crate::mail::MailExecutor; use crate::mail::MailExecutor;
use crate::ws::auth::{Authenticate, CheckAuthToken, CheckBindToken};
use crate::ws::invitations::*;
use crate::ws::issues::UpdateIssueHandler;
pub mod auth; pub mod auth;
pub mod comments; pub mod comments;
@ -88,83 +86,69 @@ impl WebSocketActor {
WsMsg::Pong => Some(WsMsg::Ping), WsMsg::Pong => Some(WsMsg::Ping),
// Issues // Issues
WsMsg::IssueUpdateRequest(id, field_id, payload) => match block_on( WsMsg::IssueUpdateRequest(id, field_id, payload) => self.handle_msg(
issues::update_issue(&self.db, &self.current_user, id, field_id, payload), UpdateIssueHandler {
) { id,
Ok(Some(msg)) => { field_id,
self.broadcast(&msg); payload,
None },
} ctx,
_ => None, )?,
}, WsMsg::IssueCreateRequest(payload) => self.handle_msg(payload, ctx)?,
WsMsg::IssueCreateRequest(payload) => { WsMsg::IssueDeleteRequest(id) => self.handle_msg(issues::DeleteIssue { id }, ctx)?,
block_on(issues::add_issue(&self.db, &self.current_user, payload))? WsMsg::ProjectIssuesRequest => self.handle_msg(issues::LoadIssues, ctx)?,
}
WsMsg::IssueDeleteRequest(id) => {
block_on(issues::delete_issue(&self.db, &self.current_user, id))?
}
WsMsg::ProjectIssuesRequest => {
block_on(issues::load_issues(&self.db, &self.current_user))?
}
// projects // projects
WsMsg::ProjectRequest => { WsMsg::ProjectRequest => self.handle_msg(projects::CurrentProject, ctx)?,
block_on(projects::current_project(&self.db, &self.current_user))? WsMsg::ProjectUpdateRequest(payload) => self.handle_msg(payload, ctx)?,
}
WsMsg::ProjectUpdateRequest(payload) => block_on(projects::update_project(
&self.db,
&self.current_user,
payload,
))?,
// auth // auth
WsMsg::AuthorizeRequest(uuid) => block_on(self.check_auth_token(uuid, ctx))?, WsMsg::AuthorizeRequest(uuid) => {
WsMsg::BindTokenCheck(uuid) => block_on(self.check_bind_token(uuid))?, self.handle_msg(CheckAuthToken { token: uuid }, ctx)?
}
WsMsg::BindTokenCheck(uuid) => {
self.handle_msg(CheckBindToken { bind_token: uuid }, ctx)?
}
WsMsg::AuthenticateRequest(email, name) => { WsMsg::AuthenticateRequest(email, name) => {
block_on(auth::authenticate(&self.db, &self.mail, name, email))? self.handle_msg(Authenticate { name, email }, ctx)?
} }
// register // register
WsMsg::SignUpRequest(email, username) => { WsMsg::SignUpRequest(email, username) => self.handle_msg(
block_on(users::register(&self.db, &self.mail, username, email))? users::Register {
} name: username,
email,
},
ctx,
)?,
// users // users
WsMsg::ProjectUsersRequest => { WsMsg::ProjectUsersRequest => self.handle_msg(users::LoadProjectUsers, ctx)?,
block_on(users::load_project_users(&self.db, &self.current_user))?
}
// comments // comments
WsMsg::IssueCommentsRequest(issue_id) => block_on(comments::load_issues( WsMsg::IssueCommentsRequest(issue_id) => {
&self.db, self.handle_msg(comments::LoadIssueComments { issue_id }, ctx)?
&self.current_user, }
issue_id, WsMsg::CreateComment(payload) => self.handle_msg(payload, ctx)?,
))?, WsMsg::UpdateComment(payload) => self.handle_msg(payload, ctx)?,
WsMsg::CommentDeleteRequest(comment_id) => {
self.handle_msg(comments::DeleteComment { comment_id }, ctx)?
}
WsMsg::CreateComment(payload) => block_on(comments::create_comment( // invitations
&self.db, WsMsg::InvitationSendRequest { name, email } => self.handle_msg(
&self.current_user, CreateInvitation {
payload, name: name.clone(),
))?, email: email.clone(),
},
ctx,
)?,
WsMsg::InvitationListRequest => self.handle_msg(ListInvitation, ctx)?,
WsMsg::InvitationAcceptRequest(id) => self.handle_msg(AcceptInvitation { id }, ctx)?,
WsMsg::UpdateComment(payload) => match block_on(comments::update_comment( WsMsg::InvitationRevokeRequest(id) => self.handle_msg(RevokeInvitation { id }, ctx)?,
&self.db,
&self.current_user,
payload,
)) {
Ok(Some(msg)) => {
self.broadcast(&msg);
None
}
_ => None,
},
WsMsg::CommentDeleteRequest(comment_id) => block_on(comments::delete_comment( WsMsg::InvitedUsersRequest => None,
&self.db,
&self.current_user,
comment_id,
))?,
// else fail // else fail
_ => { _ => {
@ -178,41 +162,6 @@ impl WebSocketActor {
Ok(msg) Ok(msg)
} }
async fn check_auth_token(
&mut self,
token: uuid::Uuid,
ctx: &mut <WebSocketActor as Actor>::Context,
) -> WsResult {
let m = match self
.db
.send(AuthorizeUser {
access_token: token,
})
.await
{
Ok(Ok(u)) => {
let user: jirs_data::User = u.into();
self.current_user = Some(user.clone());
self.join_channel(ctx.address().recipient()).await;
Some(WsMsg::AuthorizeLoaded(Ok(user)))
}
Ok(Err(_)) => Some(WsMsg::AuthorizeLoaded(
Err("Invalid auth token".to_string()),
)),
_ => Some(WsMsg::AuthorizeExpired),
};
Ok(m)
}
async fn check_bind_token(&mut self, bind_token: uuid::Uuid) -> WsResult {
let token: Token = match self.db.send(FindBindToken { token: bind_token }).await {
Ok(Ok(token)) => token,
Ok(Err(_)) => return Ok(Some(WsMsg::BindTokenBad)),
_ => return Ok(None),
};
Ok(Some(WsMsg::BindTokenOk(token.access_token)))
}
async fn join_channel(&self, addr: Recipient<InnerMsg>) { async fn join_channel(&self, addr: Recipient<InnerMsg>) {
info!("joining channel..."); info!("joining channel...");
info!(" current user {:?}", self.current_user); info!(" current user {:?}", self.current_user);
@ -267,35 +216,11 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
} }
} }
impl WebSocketActor { pub trait WsHandler<Message>
fn try_handle_message( where
&mut self, Self: Actor,
msg: WsMsg, {
_ctx: &mut <WebSocketActor as Actor>::Context, fn handle_msg(&mut self, msg: Message, _ctx: &mut <Self as Actor>::Context) -> WsResult;
) -> WsResult
where
Self: Actor,
{
match msg {
WsMsg::InvitationSendRequest { name, email } => {
use invitations::*;
let m = CreateInvitation {
name: name.clone(),
email: email.clone(),
};
// Handler::handle(&mut self, m, _ctx);
Ok(None)
// Handler::<CreateInvitation>::handle(&mut self, m, _ctx)
// <self as Handler<CreateInvitation>>.handle(m, ctx)
}
// WsMsg::InvitationListRequest => self.handle(ListInvitation, ctx),
// WsMsg::InvitationAcceptRequest(id) => Ok(None),
// WsMsg::InvitationRevokeRequest(id) => self.handle(RevokeInvitation { id: *id }, ctx),
// WsMsg::InvitedUsersRequest => Ok(None),
_ => Ok(None),
}
}
} }
#[derive(Message, Debug)] #[derive(Message, Debug)]

View File

@ -1,50 +1,44 @@
use actix::Addr; use futures::executor::block_on;
use actix_web::web::Data;
use jirs_data::{UpdateProjectPayload, WsMsg}; use jirs_data::{UpdateProjectPayload, WsMsg};
use crate::db::projects::LoadCurrentProject; use crate::db::projects::LoadCurrentProject;
use crate::db::DbExecutor; use crate::ws::{WebSocketActor, WsHandler, WsResult};
use crate::ws::{current_user, WsResult};
pub async fn current_project( pub struct CurrentProject;
db: &Data<Addr<DbExecutor>>,
user: &Option<jirs_data::User>,
) -> WsResult {
let project_id = current_user(user).map(|u| u.project_id)?;
let m = match db.send(LoadCurrentProject { project_id }).await { impl WsHandler<CurrentProject> for WebSocketActor {
Ok(Ok(project)) => Some(WsMsg::ProjectLoaded(project.into())), fn handle_msg(&mut self, _msg: CurrentProject, _ctx: &mut Self::Context) -> WsResult {
Ok(Err(e)) => { let project_id = self.require_user()?.project_id;
error!("{:?}", e);
None let m = match block_on(self.db.send(LoadCurrentProject { project_id })) {
} Ok(Ok(project)) => Some(WsMsg::ProjectLoaded(project.into())),
Err(e) => { Ok(Err(e)) => {
error!("{:?}", e); error!("{:?}", e);
None None
} }
}; Err(e) => {
Ok(m) error!("{:?}", e);
None
}
};
Ok(m)
}
} }
pub async fn update_project( impl WsHandler<UpdateProjectPayload> for WebSocketActor {
db: &Data<Addr<DbExecutor>>, fn handle_msg(&mut self, msg: UpdateProjectPayload, _ctx: &mut Self::Context) -> WsResult {
user: &Option<jirs_data::User>, let project_id = self.require_user()?.project_id;
payload: UpdateProjectPayload, let project = match block_on(self.db.send(crate::db::projects::UpdateProject {
) -> WsResult {
let project_id = current_user(user).map(|u| u.project_id)?;
let project = match db
.send(crate::db::projects::UpdateProject {
project_id, project_id,
name: payload.name, name: msg.name,
url: payload.url, url: msg.url,
description: payload.description, description: msg.description,
category: payload.category, category: msg.category,
}) })) {
.await Ok(Ok(project)) => project,
{ _ => return Ok(None),
Ok(Ok(project)) => project, };
_ => return Ok(None), Ok(Some(WsMsg::ProjectLoaded(project.into())))
}; }
Ok(Some(WsMsg::ProjectLoaded(project.into())))
} }

View File

@ -1,50 +1,50 @@
use actix::Addr; use futures::executor::block_on;
use actix_web::web::Data;
use jirs_data::WsMsg; use jirs_data::WsMsg;
use crate::db::users::{LoadProjectUsers, Register}; use crate::db::users::Register as DbRegister;
use crate::db::DbExecutor; use crate::ws::auth::Authenticate;
use crate::mail::MailExecutor; use crate::ws::{current_user, WebSocketActor, WsHandler, WsResult};
use crate::ws::auth::authenticate;
use crate::ws::{current_user, WsResult};
pub async fn load_project_users( pub struct LoadProjectUsers;
db: &Data<Addr<DbExecutor>>,
user: &Option<jirs_data::User>, impl WsHandler<LoadProjectUsers> for WebSocketActor {
) -> WsResult { fn handle_msg(&mut self, _msg: LoadProjectUsers, _ctx: &mut Self::Context) -> WsResult {
let project_id = current_user(user).map(|u| u.project_id)?; use crate::db::users::LoadProjectUsers as Msg;
let m = match db.send(LoadProjectUsers { project_id }).await {
Ok(Ok(v)) => Some(WsMsg::ProjectUsersLoaded( let project_id = current_user(&self.current_user).map(|u| u.project_id)?;
v.into_iter().map(|i| i.into()).collect(), let m = match block_on(self.db.send(Msg { project_id })) {
)), Ok(Ok(v)) => Some(WsMsg::ProjectUsersLoaded(
_ => None, v.into_iter().map(|i| i.into()).collect(),
}; )),
Ok(m) _ => None,
};
Ok(m)
}
} }
pub async fn register( pub struct Register {
db: &Data<Addr<DbExecutor>>, pub name: String,
mail: &Data<Addr<MailExecutor>>, pub email: String,
name: String, }
email: String,
) -> WsResult { impl WsHandler<Register> for WebSocketActor {
let msg = match db fn handle_msg(&mut self, msg: Register, ctx: &mut Self::Context) -> WsResult {
.send(Register { let Register { name, email } = msg;
let msg = match block_on(self.db.send(DbRegister {
name: name.clone(), name: name.clone(),
email: email.clone(), email: email.clone(),
}) })) {
.await Ok(Ok(_)) => Some(WsMsg::SignUpSuccess),
{ Ok(Err(_)) => Some(WsMsg::SignUpPairTaken),
Ok(Ok(_)) => Some(WsMsg::SignUpSuccess), _ => None,
Ok(Err(_)) => Some(WsMsg::SignUpPairTaken), };
_ => None,
};
match authenticate(db, mail, name, email).await { match self.handle_msg(Authenticate { name, email }, ctx) {
Ok(_) => (), Ok(_) => (),
Err(e) => return Ok(Some(e)), Err(e) => return Ok(Some(e)),
}; };
Ok(msg) Ok(msg)
}
} }