Improve accept invitation
This commit is contained in:
parent
e618a4f23c
commit
3cb74084d9
94
.dockerignore
Normal file
94
.dockerignore
Normal file
@ -0,0 +1,94 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Rust template
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
/tmp/
|
||||
|
||||
/jirs-client/target/
|
||||
/jirs-client/tmp/
|
||||
/jirs-client/build/
|
||||
|
||||
/jirs-server/target/
|
||||
/jirs-server/tmp/
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,4 +7,7 @@ db.toml
|
||||
db.test.toml
|
||||
pkg
|
||||
jirs-client/pkg
|
||||
jirs-client/tmp
|
||||
jirs-client/build
|
||||
tmp
|
||||
jirs-server/target
|
||||
|
@ -1,7 +1,11 @@
|
||||
version: '3.0'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- 5432:5432
|
||||
version: '3.0'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- 5432:5432
|
||||
server:
|
||||
build:
|
||||
dockerfile: ./jirs-server/Dockerfile
|
||||
context: .
|
||||
|
1
jirs-client/.gitignore
vendored
1
jirs-client/.gitignore
vendored
@ -5,3 +5,4 @@ dist
|
||||
tmp
|
||||
dev/styles.css
|
||||
build
|
||||
target
|
||||
|
20
jirs-client/js/css/invite.css
Normal file
20
jirs-client/js/css/invite.css
Normal file
@ -0,0 +1,20 @@
|
||||
#invite > .styledForm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto 24px;
|
||||
width: 400px;
|
||||
background: rgb(255, 255, 255) none repeat scroll 0 0;
|
||||
border-radius: 3px;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0 0 10px;
|
||||
box-sizing: border-box;
|
||||
color: var(--textMedium);
|
||||
}
|
||||
|
||||
#invite > .styledForm:first-of-type {
|
||||
margin-top: 124.5px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#invite > .styledForm:last-of-type {
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 10px;
|
||||
}
|
@ -30,3 +30,4 @@
|
||||
@import "./css/login.css";
|
||||
@import "./css/register.css";
|
||||
@import "./css/users.css";
|
||||
@import "./css/invite.css";
|
||||
|
@ -3,9 +3,10 @@
|
||||
. .env
|
||||
|
||||
rm -Rf tmp
|
||||
mkdir tmp
|
||||
mkdir -p tmp
|
||||
mkdir -p target
|
||||
|
||||
wasm-pack build --mode normal --dev --out-name jirs --out-dir ./tmp --target web
|
||||
wasm-pack build --mode normal --dev --out-name jirs --out-dir ./tmp --target web -- --verbose
|
||||
../target/debug/jirs-css -i ./js/styles.css -O ./tmp/styles.css
|
||||
|
||||
cp -r ./static/* ./tmp
|
||||
|
@ -50,12 +50,18 @@ pub enum ProfilePageChange {
|
||||
SubmitForm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum InvitationPageChange {
|
||||
SubmitForm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PageChanged {
|
||||
Users(UsersPageChange),
|
||||
ProjectSettings(ProjectPageChange),
|
||||
Profile(ProfilePageChange),
|
||||
Board(BoardPageChange),
|
||||
Invitation(InvitationPageChange),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,34 +1,61 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::InviteFieldId;
|
||||
use jirs_data::{InviteFieldId, WsMsg};
|
||||
|
||||
use crate::model::{InvitePage, Model, Page, PageContent};
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_field::StyledField;
|
||||
use crate::shared::styled_form::StyledForm;
|
||||
use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::{outer_layout, ToNode};
|
||||
use crate::validations::is_token;
|
||||
use crate::{FieldId, Msg};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{FieldId, InvitationPageChange, Msg, PageChanged, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
||||
if let Msg::ChangePage(Page::Project) = msg {
|
||||
build_page_content(model);
|
||||
return;
|
||||
}
|
||||
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
match model.page_content {
|
||||
PageContent::Invite(..) => (),
|
||||
_ if model.page == Page::Invite => build_page_content(model),
|
||||
_ => (),
|
||||
};
|
||||
|
||||
let page = match &mut model.page_content {
|
||||
PageContent::Invite(page) => page,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Msg::StrInputChanged(FieldId::Invite(InviteFieldId::Token), text) = msg {
|
||||
page.token_touched = true;
|
||||
page.token = text;
|
||||
match msg {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::InvitationAcceptFailure(_))) => {
|
||||
page.error = Some("Invalid token".to_string());
|
||||
}
|
||||
Msg::StrInputChanged(FieldId::Invite(InviteFieldId::Token), text) => {
|
||||
page.token_touched = true;
|
||||
page.token = text;
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Invitation(InvitationPageChange::SubmitForm)) => {
|
||||
if let Ok(token) = uuid::Uuid::from_str(page.token.as_str()) {
|
||||
send_ws_msg(
|
||||
WsMsg::InvitationAcceptRequest(token),
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
page.error = None;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_page_content(model: &mut Model) {
|
||||
model.page_content = PageContent::Invite(Box::new(InvitePage::default()));
|
||||
let s: String = seed::document().location().unwrap().to_string().into();
|
||||
let url = seed::Url::from_str(s.as_str()).unwrap();
|
||||
let search = url.search();
|
||||
let values = search.get("token").map(|v| v.clone()).unwrap_or_default();
|
||||
let mut content = InvitePage::default();
|
||||
content.token = values.get(0).map(|s| s.clone()).unwrap_or_default();
|
||||
model.page_content = PageContent::Invite(Box::new(content));
|
||||
}
|
||||
|
||||
pub fn view(model: &Model) -> Node<Msg> {
|
||||
@ -37,21 +64,46 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
_ => return empty![],
|
||||
};
|
||||
|
||||
let token = StyledInput::build(FieldId::Invite(InviteFieldId::Token))
|
||||
.valid(!page.token_touched || is_token(page.token.as_str()))
|
||||
.build()
|
||||
.into_node();
|
||||
let token_field = StyledField::build()
|
||||
.input(token)
|
||||
.label("Your invite token")
|
||||
.build()
|
||||
.into_node();
|
||||
let token_field = token_field(page);
|
||||
let submit_field = submit(page);
|
||||
let error = match page.error.as_ref() {
|
||||
Some(s) => div![class!["error"], s.as_str()],
|
||||
_ => empty![],
|
||||
};
|
||||
|
||||
let form = StyledForm::build()
|
||||
.heading("Welcome in JIRS")
|
||||
.on_submit(ev(Ev::Submit, move |ev| {
|
||||
ev.prevent_default();
|
||||
Msg::PageChanged(PageChanged::Invitation(InvitationPageChange::SubmitForm))
|
||||
}))
|
||||
.add_field(token_field)
|
||||
.add_field(submit_field)
|
||||
.add_field(error)
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
outer_layout(model, "invite", vec![form])
|
||||
}
|
||||
|
||||
fn submit(_page: &Box<InvitePage>) -> Node<Msg> {
|
||||
let submit = StyledButton::build()
|
||||
.text("Accept")
|
||||
.primary()
|
||||
.build()
|
||||
.into_node();
|
||||
StyledField::build().input(submit).build().into_node()
|
||||
}
|
||||
|
||||
fn token_field(page: &Box<InvitePage>) -> Node<Msg> {
|
||||
let token = StyledInput::build(FieldId::Invite(InviteFieldId::Token))
|
||||
.valid(!page.token_touched || is_token(page.token.as_str()))
|
||||
.value(page.token.as_str())
|
||||
.build()
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.input(token)
|
||||
.label("Your invite token")
|
||||
.build()
|
||||
.into_node()
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ pub enum Msg {
|
||||
InviteRequest,
|
||||
InviteRevokeRequest(InvitationId),
|
||||
InviteApproveRequest(InvitationId),
|
||||
InvitedUserRemove(EmailString),
|
||||
InvitedUserRemove(UserId),
|
||||
|
||||
// sign up
|
||||
SignUpRequest,
|
||||
|
@ -62,6 +62,11 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
|
||||
model.modals.push(ModalType::DebugModal);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
Msg::GlobalKeyDown { key, .. } if key.eq(">") => {
|
||||
log!(model);
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
add_issue::update(msg, model, orders);
|
||||
|
@ -262,6 +262,7 @@ pub struct ProjectPage {
|
||||
pub struct InvitePage {
|
||||
pub token: String,
|
||||
pub token_touched: bool,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -77,10 +77,12 @@ pub fn inner_layout(
|
||||
]
|
||||
}
|
||||
|
||||
pub fn outer_layout(_model: &Model, page_name: &str, children: Vec<Node<Msg>>) -> Node<Msg> {
|
||||
pub fn outer_layout(model: &Model, page_name: &str, children: Vec<Node<Msg>>) -> Node<Msg> {
|
||||
let modal = crate::modal::view(model);
|
||||
article![
|
||||
class!["outer-layout", "outerPage"],
|
||||
id![page_name],
|
||||
modal,
|
||||
children
|
||||
]
|
||||
}
|
||||
|
@ -45,11 +45,11 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref(), orders);
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitedUserRemoveSuccess(email)) => {
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitedUserRemoveSuccess(removed_id)) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut page.invited_users, &mut old);
|
||||
for user in old {
|
||||
if user.email != email {
|
||||
if user.id != removed_id {
|
||||
page.invited_users.push(user);
|
||||
}
|
||||
}
|
||||
@ -110,9 +110,9 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
orders,
|
||||
);
|
||||
}
|
||||
Msg::InvitedUserRemove(email) => {
|
||||
Msg::InvitedUserRemove(user_id) => {
|
||||
send_ws_msg(
|
||||
WsMsg::InvitedUserRemoveRequest(email),
|
||||
WsMsg::InvitedUserRemoveRequest(user_id),
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
|
@ -108,10 +108,12 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.invited_users
|
||||
.iter()
|
||||
.map(|user| {
|
||||
let email = user.email.clone();
|
||||
let user_id = user.id;
|
||||
let remove = StyledButton::build()
|
||||
.text("Remove")
|
||||
.on_click(mouse_ev(Ev::Click, move |_| Msg::InvitedUserRemove(email)))
|
||||
.on_click(mouse_ev(Ev::Click, move |_| {
|
||||
Msg::InvitedUserRemove(user_id)
|
||||
}))
|
||||
.build()
|
||||
.into_node();
|
||||
let role = page
|
||||
|
@ -31,6 +31,8 @@ pub type MessageId = i32;
|
||||
pub type EmailString = String;
|
||||
pub type UsernameString = String;
|
||||
pub type TitleString = String;
|
||||
pub type BindToken = Uuid;
|
||||
pub type InvitationToken = Uuid;
|
||||
|
||||
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
||||
#[cfg_attr(feature = "backend", sql_type = "IssueTypeType")]
|
||||
@ -705,10 +707,11 @@ pub enum WsMsg {
|
||||
InvitationRevokeRequest(InvitationId),
|
||||
InvitationRevokeSuccess(InvitationId),
|
||||
//
|
||||
InvitationAcceptRequest(InvitationId),
|
||||
InvitationAcceptSuccess(InvitationId),
|
||||
InvitedUserRemoveRequest(EmailString),
|
||||
InvitedUserRemoveSuccess(EmailString),
|
||||
InvitationAcceptRequest(InvitationToken),
|
||||
InvitationAcceptSuccess(BindToken),
|
||||
InvitationAcceptFailure(InvitationToken),
|
||||
InvitedUserRemoveRequest(UserId),
|
||||
InvitedUserRemoveSuccess(UserId),
|
||||
|
||||
// project page
|
||||
ProjectRequest,
|
||||
|
15
jirs-server/Dockerfile
Normal file
15
jirs-server/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
FROM archlinux:latest
|
||||
|
||||
WORKDIR /app/
|
||||
|
||||
RUN pacman -Sy rustup gcc postgresql --noconfirm
|
||||
|
||||
ADD jirs-server .
|
||||
ADD jirs-data .
|
||||
|
||||
RUN rustup toolchain install nightly && \
|
||||
rustup default nightly && \
|
||||
cargo install diesel_cli --no-default-features --features postgres && \
|
||||
cd jirs-server && diesel setup
|
||||
|
||||
CMD cd jirs-server && cargo run --bin jirs_server
|
@ -3,8 +3,8 @@ use diesel::pg::Pg;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use jirs_data::{
|
||||
EmailString, Invitation, InvitationId, InvitationState, ProjectId, User, UserId, UserRole,
|
||||
UsernameString,
|
||||
EmailString, Invitation, InvitationId, InvitationState, InvitationToken, ProjectId, Token,
|
||||
User, UserId, UserRole, UsernameString,
|
||||
};
|
||||
|
||||
use crate::db::DbExecutor;
|
||||
@ -139,15 +139,15 @@ impl Handler<RevokeInvitation> for DbExecutor {
|
||||
}
|
||||
|
||||
pub struct AcceptInvitation {
|
||||
pub id: InvitationId,
|
||||
pub invitation_token: InvitationToken,
|
||||
}
|
||||
|
||||
impl Message for AcceptInvitation {
|
||||
type Result = Result<User, ServiceErrors>;
|
||||
type Result = Result<Token, ServiceErrors>;
|
||||
}
|
||||
|
||||
impl Handler<AcceptInvitation> for DbExecutor {
|
||||
type Result = Result<User, ServiceErrors>;
|
||||
type Result = Result<Token, ServiceErrors>;
|
||||
|
||||
fn handle(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> Self::Result {
|
||||
use crate::schema::invitations::dsl::*;
|
||||
@ -157,7 +157,7 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
||||
.get()
|
||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||
|
||||
let query = invitations.find(msg.id);
|
||||
let query = invitations.filter(bind_token.eq(msg.invitation_token));
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||
let invitation: Invitation = query
|
||||
.first(conn)
|
||||
@ -206,6 +206,15 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
||||
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||
};
|
||||
|
||||
Ok(user)
|
||||
let token = {
|
||||
use crate::schema::tokens::dsl::*;
|
||||
let query = tokens.filter(user_id.eq(user.id));
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||
query
|
||||
.first(conn)
|
||||
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?
|
||||
};
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use actix::{Handler, Message};
|
||||
use diesel::pg::Pg;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use jirs_data::{UserId, UserProject, UserProjectId};
|
||||
use jirs_data::{ProjectId, UserId, UserProject, UserProjectId, UserRole};
|
||||
|
||||
use crate::db::DbExecutor;
|
||||
use crate::errors::ServiceErrors;
|
||||
@ -109,3 +109,61 @@ impl Handler<ChangeCurrentUserProject> for DbExecutor {
|
||||
Ok(user_project)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RemoveInvitedUser {
|
||||
pub invited_id: UserId,
|
||||
pub inviter_id: UserId,
|
||||
pub project_id: ProjectId,
|
||||
}
|
||||
|
||||
impl Message for RemoveInvitedUser {
|
||||
type Result = Result<(), ServiceErrors>;
|
||||
}
|
||||
|
||||
impl Handler<RemoveInvitedUser> for DbExecutor {
|
||||
type Result = Result<(), ServiceErrors>;
|
||||
|
||||
fn handle(&mut self, msg: RemoveInvitedUser, _ctx: &mut Self::Context) -> Self::Result {
|
||||
use crate::schema::user_projects::dsl::*;
|
||||
|
||||
let conn = &self
|
||||
.pool
|
||||
.get()
|
||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||
|
||||
if msg.invited_id == msg.inviter_id {
|
||||
return Err(ServiceErrors::Unauthorized);
|
||||
}
|
||||
|
||||
{
|
||||
let owner = UserRole::Owner;
|
||||
let query = user_projects.filter(
|
||||
user_id
|
||||
.eq(msg.inviter_id)
|
||||
.and(project_id.eq(msg.project_id))
|
||||
.and(role.eq(owner)),
|
||||
);
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||
query
|
||||
.first::<UserProject>(conn)
|
||||
.map_err(|_e| ServiceErrors::Unauthorized)?;
|
||||
}
|
||||
|
||||
{
|
||||
let query = diesel::delete(user_projects).filter(
|
||||
user_id
|
||||
.eq(msg.invited_id)
|
||||
.and(project_id.eq(msg.project_id)),
|
||||
);
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||
query.execute(conn).map_err(|_e| {
|
||||
ServiceErrors::RecordNotFound(format!(
|
||||
"user project user with id {} for project {}",
|
||||
msg.invited_id, msg.project_id
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use futures::executor::block_on;
|
||||
|
||||
use jirs_data::{EmailString, InvitationId, UserRole, UsernameString, WsMsg};
|
||||
use jirs_data::{EmailString, InvitationId, InvitationToken, UserRole, UsernameString, WsMsg};
|
||||
|
||||
use crate::db::invitations;
|
||||
use crate::ws::{WebSocketActor, WsHandler, WsResult};
|
||||
@ -131,22 +131,23 @@ impl WsHandler<RevokeInvitation> for WebSocketActor {
|
||||
}
|
||||
|
||||
pub struct AcceptInvitation {
|
||||
pub id: InvitationId,
|
||||
pub invitation_token: InvitationToken,
|
||||
}
|
||||
|
||||
impl WsHandler<AcceptInvitation> for WebSocketActor {
|
||||
fn handle_msg(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> WsResult {
|
||||
self.require_user()?;
|
||||
let AcceptInvitation { id } = msg;
|
||||
let res = match block_on(self.db.send(invitations::AcceptInvitation { id })) {
|
||||
Ok(Ok(_)) => Some(WsMsg::InvitationAcceptSuccess(id)),
|
||||
let AcceptInvitation { invitation_token } = msg;
|
||||
let res = match block_on(self.db.send(invitations::AcceptInvitation {
|
||||
invitation_token: invitation_token.clone(),
|
||||
})) {
|
||||
Ok(Ok(token)) => Some(WsMsg::InvitationAcceptSuccess(token.access_token)),
|
||||
Ok(Err(e)) => {
|
||||
error!("{:?}", e);
|
||||
return Ok(None);
|
||||
Some(WsMsg::InvitationAcceptFailure(invitation_token))
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
return Ok(None);
|
||||
Some(WsMsg::InvitationAcceptFailure(invitation_token))
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
|
@ -150,6 +150,9 @@ impl WebSocketActor {
|
||||
|
||||
// users
|
||||
WsMsg::ProjectUsersRequest => self.handle_msg(LoadProjectUsers, ctx)?,
|
||||
WsMsg::InvitedUserRemoveRequest(user_id) => {
|
||||
self.handle_msg(RemoveInvitedUser { user_id }, ctx)?
|
||||
}
|
||||
|
||||
// comments
|
||||
WsMsg::IssueCommentsRequest(issue_id) => {
|
||||
@ -166,7 +169,9 @@ impl WebSocketActor {
|
||||
self.handle_msg(CreateInvitation { name, email, role }, ctx)?
|
||||
}
|
||||
WsMsg::InvitationListRequest => self.handle_msg(ListInvitation, ctx)?,
|
||||
WsMsg::InvitationAcceptRequest(id) => self.handle_msg(AcceptInvitation { id }, ctx)?,
|
||||
WsMsg::InvitationAcceptRequest(invitation_token) => {
|
||||
self.handle_msg(AcceptInvitation { invitation_token }, ctx)?
|
||||
}
|
||||
WsMsg::InvitationRevokeRequest(id) => self.handle_msg(RevokeInvitation { id }, ctx)?,
|
||||
WsMsg::InvitedUsersRequest => self.handle_msg(LoadInvitedUsers, ctx)?,
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use futures::executor::block_on;
|
||||
|
||||
use jirs_data::WsMsg;
|
||||
use jirs_data::{UserId, UserProject, WsMsg};
|
||||
|
||||
use crate::db;
|
||||
use crate::db::users::Register as DbRegister;
|
||||
use crate::ws::auth::Authenticate;
|
||||
use crate::ws::{WebSocketActor, WsHandler, WsResult};
|
||||
@ -101,3 +102,35 @@ impl WsHandler<ProfileUpdate> for WebSocketActor {
|
||||
Ok(Some(WsMsg::ProfileUpdated))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RemoveInvitedUser {
|
||||
pub user_id: UserId,
|
||||
}
|
||||
|
||||
impl WsHandler<RemoveInvitedUser> for WebSocketActor {
|
||||
fn handle_msg(&mut self, msg: RemoveInvitedUser, _ctx: &mut Self::Context) -> WsResult {
|
||||
let RemoveInvitedUser {
|
||||
user_id: invited_id,
|
||||
} = msg;
|
||||
let UserProject {
|
||||
user_id: inviter_id,
|
||||
project_id,
|
||||
..
|
||||
} = self.require_user_project()?.clone();
|
||||
match block_on(self.db.send(db::user_projects::RemoveInvitedUser {
|
||||
invited_id,
|
||||
inviter_id,
|
||||
project_id,
|
||||
})) {
|
||||
Ok(Ok(_users)) => Ok(Some(WsMsg::InvitedUserRemoveSuccess(invited_id))),
|
||||
Ok(Err(e)) => {
|
||||
error!("{:?}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user