Add multi project support.
This commit is contained in:
parent
2d55c5f143
commit
372b9d9b8d
9
.cargo/config
Normal file
9
.cargo/config
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[target.x86_64-unknown-linux-gnu]
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-fuse-ld=lld",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.nightly-x86_64-unknown-linux-gnu]
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-fuse-ld=lld",
|
||||||
|
]
|
@ -12,7 +12,7 @@ use crate::{FieldId, Msg};
|
|||||||
|
|
||||||
pub fn update(msg: Msg, model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: Msg, model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
||||||
if let Msg::ChangePage(Page::Project) = msg {
|
if let Msg::ChangePage(Page::Project) = msg {
|
||||||
model.page_content = PageContent::Invite(Box::new(InvitePage::default()));
|
build_page_content(model);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +27,10 @@ pub fn update(msg: Msg, model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_page_content(model: &mut Model) {
|
||||||
|
model.page_content = PageContent::Invite(Box::new(InvitePage::default()));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view(model: &Model) -> Node<Msg> {
|
pub fn view(model: &Model) -> Node<Msg> {
|
||||||
let page = match &model.page_content {
|
let page = match &model.page_content {
|
||||||
PageContent::Invite(page) => page,
|
PageContent::Invite(page) => page,
|
||||||
|
@ -175,6 +175,7 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
crate::modal::update(&msg, model, orders);
|
crate::modal::update(&msg, model, orders);
|
||||||
|
crate::shared::aside::update(&msg, model, orders);
|
||||||
match model.page {
|
match model.page {
|
||||||
Page::Project | Page::AddIssue | Page::EditIssue(..) => project::update(msg, model, orders),
|
Page::Project | Page::AddIssue | Page::EditIssue(..) => project::update(msg, model, orders),
|
||||||
Page::ProjectSettings => project_settings::update(msg, model, orders),
|
Page::ProjectSettings => project_settings::update(msg, model, orders),
|
||||||
|
@ -458,11 +458,14 @@ pub struct Model {
|
|||||||
|
|
||||||
pub project: Option<Project>,
|
pub project: Option<Project>,
|
||||||
pub user: Option<User>,
|
pub user: Option<User>,
|
||||||
|
pub current_user_project: Option<UserProject>,
|
||||||
pub issues: Vec<Issue>,
|
pub issues: Vec<Issue>,
|
||||||
pub users: Vec<User>,
|
pub users: Vec<User>,
|
||||||
pub comments: Vec<Comment>,
|
pub comments: Vec<Comment>,
|
||||||
pub issue_statuses: Vec<IssueStatus>,
|
pub issue_statuses: Vec<IssueStatus>,
|
||||||
pub messages: Vec<Message>,
|
pub messages: Vec<Message>,
|
||||||
|
pub user_projects: Vec<UserProject>,
|
||||||
|
pub projects: Vec<Project>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
@ -475,8 +478,6 @@ impl Model {
|
|||||||
issue_form: None,
|
issue_form: None,
|
||||||
project_form: None,
|
project_form: None,
|
||||||
comment_form: None,
|
comment_form: None,
|
||||||
issues: vec![],
|
|
||||||
users: vec![],
|
|
||||||
comments_by_project_id: Default::default(),
|
comments_by_project_id: Default::default(),
|
||||||
page: Page::Project,
|
page: Page::Project,
|
||||||
host_url,
|
host_url,
|
||||||
@ -484,9 +485,12 @@ impl Model {
|
|||||||
page_content: PageContent::Project(Box::new(ProjectPage::default())),
|
page_content: PageContent::Project(Box::new(ProjectPage::default())),
|
||||||
modals: vec![],
|
modals: vec![],
|
||||||
project: None,
|
project: None,
|
||||||
comments: vec![],
|
current_user_project: None,
|
||||||
about_tooltip_visible: false,
|
about_tooltip_visible: false,
|
||||||
messages_tooltip_visible: false,
|
messages_tooltip_visible: false,
|
||||||
|
issues: vec![],
|
||||||
|
users: vec![],
|
||||||
|
comments: vec![],
|
||||||
issue_statuses: vec![],
|
issue_statuses: vec![],
|
||||||
messages: vec![Message {
|
messages: vec![Message {
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -499,6 +503,22 @@ impl Model {
|
|||||||
created_at: chrono::NaiveDateTime::from_timestamp(4567890, 123),
|
created_at: chrono::NaiveDateTime::from_timestamp(4567890, 123),
|
||||||
updated_at: chrono::NaiveDateTime::from_timestamp(1234567, 098),
|
updated_at: chrono::NaiveDateTime::from_timestamp(1234567, 098),
|
||||||
}],
|
}],
|
||||||
|
user_projects: vec![],
|
||||||
|
projects: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_user_role(&self) -> UserRole {
|
||||||
|
self.current_user_project
|
||||||
|
.as_ref()
|
||||||
|
.map(|up| up.role)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_project_id(&self) -> ProjectId {
|
||||||
|
self.current_user_project
|
||||||
|
.as_ref()
|
||||||
|
.map(|up| up.project_id)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,8 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
match msg {
|
match msg {
|
||||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
||||||
| Msg::ChangePage(Page::Profile) => {
|
| Msg::ChangePage(Page::Profile) => {
|
||||||
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref(), orders);
|
init_load(model, orders);
|
||||||
let user = match model.user {
|
build_page_content(model);
|
||||||
Some(ref user) => user,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
model.page_content = PageContent::Profile(Box::new(ProfilePage::new(user)));
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -77,6 +73,18 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
|
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref(), orders);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_page_content(model: &mut Model) {
|
||||||
|
let user = match model.user {
|
||||||
|
Some(ref user) => user,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
model.page_content = PageContent::Profile(Box::new(ProfilePage::new(user)));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view(model: &Model) -> Node<Msg> {
|
pub fn view(model: &Model) -> Node<Msg> {
|
||||||
let page = match &model.page_content {
|
let page = match &model.page_content {
|
||||||
PageContent::Profile(profile_page) => profile_page,
|
PageContent::Profile(profile_page) => profile_page,
|
||||||
|
@ -22,7 +22,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
Msg::ChangePage(Page::Project)
|
Msg::ChangePage(Page::Project)
|
||||||
| Msg::ChangePage(Page::AddIssue)
|
| Msg::ChangePage(Page::AddIssue)
|
||||||
| Msg::ChangePage(Page::EditIssue(..)) => {
|
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||||
model.page_content = PageContent::Project(Box::new(ProjectPage::default()));
|
build_page_content(model);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -37,16 +37,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
| Msg::ChangePage(Page::Project)
|
| Msg::ChangePage(Page::Project)
|
||||||
| Msg::ChangePage(Page::AddIssue)
|
| Msg::ChangePage(Page::AddIssue)
|
||||||
| Msg::ChangePage(Page::EditIssue(..)) => {
|
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||||
enqueue_ws_msg(
|
init_load(model, orders);
|
||||||
vec![
|
|
||||||
jirs_data::WsMsg::ProjectRequest,
|
|
||||||
jirs_data::WsMsg::ProjectIssuesRequest,
|
|
||||||
jirs_data::WsMsg::ProjectUsersRequest,
|
|
||||||
jirs_data::WsMsg::IssueStatusesRequest,
|
|
||||||
],
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
|
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
|
||||||
let mut old: Vec<Issue> = vec![];
|
let mut old: Vec<Issue> = vec![];
|
||||||
@ -139,6 +130,23 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
|
enqueue_ws_msg(
|
||||||
|
vec![
|
||||||
|
jirs_data::WsMsg::ProjectRequest,
|
||||||
|
jirs_data::WsMsg::ProjectIssuesRequest,
|
||||||
|
jirs_data::WsMsg::ProjectUsersRequest,
|
||||||
|
jirs_data::WsMsg::IssueStatusesRequest,
|
||||||
|
],
|
||||||
|
model.ws.as_ref(),
|
||||||
|
orders,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_page_content(model: &mut Model) {
|
||||||
|
model.page_content = PageContent::Project(Box::new(ProjectPage::default()));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view(model: &Model) -> Node<Msg> {
|
pub fn view(model: &Model) -> Node<Msg> {
|
||||||
let project_section = vec![
|
let project_section = vec![
|
||||||
breadcrumbs(model),
|
breadcrumbs(model),
|
||||||
|
@ -391,15 +391,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
match msg {
|
match msg {
|
||||||
Msg::WebSocketChange(ref change) => match change {
|
Msg::WebSocketChange(ref change) => match change {
|
||||||
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
|
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
|
||||||
enqueue_ws_msg(
|
init_load(model, orders);
|
||||||
vec![
|
|
||||||
WsMsg::ProjectRequest,
|
|
||||||
WsMsg::IssueStatusesRequest,
|
|
||||||
WsMsg::ProjectIssuesRequest,
|
|
||||||
],
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => {
|
WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => {
|
||||||
build_page_content(model);
|
build_page_content(model);
|
||||||
@ -417,15 +409,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
Msg::ChangePage(Page::ProjectSettings) => {
|
Msg::ChangePage(Page::ProjectSettings) => {
|
||||||
build_page_content(model);
|
build_page_content(model);
|
||||||
if model.user.is_some() {
|
if model.user.is_some() {
|
||||||
enqueue_ws_msg(
|
init_load(model, orders);
|
||||||
vec![
|
|
||||||
WsMsg::ProjectRequest,
|
|
||||||
WsMsg::IssueStatusesRequest,
|
|
||||||
WsMsg::ProjectIssuesRequest,
|
|
||||||
],
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -547,6 +531,18 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
|
enqueue_ws_msg(
|
||||||
|
vec![
|
||||||
|
WsMsg::ProjectRequest,
|
||||||
|
WsMsg::IssueStatusesRequest,
|
||||||
|
WsMsg::ProjectIssuesRequest,
|
||||||
|
],
|
||||||
|
model.ws.as_ref(),
|
||||||
|
orders,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {
|
fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {
|
||||||
let page = match &mut model.page_content {
|
let page = match &mut model.page_content {
|
||||||
PageContent::ProjectSettings(page) => page,
|
PageContent::ProjectSettings(page) => page,
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use jirs_data::UserRole;
|
use jirs_data::{UserRole, WsMsg};
|
||||||
|
|
||||||
use crate::model::{Model, Page};
|
use crate::model::{Model, Page};
|
||||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||||
use crate::shared::{divider, ToNode};
|
use crate::shared::{divider, ToNode};
|
||||||
use crate::Msg;
|
use crate::ws::enqueue_ws_msg;
|
||||||
|
use crate::{Msg, WebSocketChanged};
|
||||||
|
|
||||||
|
pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
|
match msg {
|
||||||
|
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(Ok(_)))) => {
|
||||||
|
enqueue_ws_msg(
|
||||||
|
vec![WsMsg::UserProjectLoad, WsMsg::ProjectsLoad],
|
||||||
|
model.ws.as_ref(),
|
||||||
|
orders,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render(model: &Model) -> Node<Msg> {
|
pub fn render(model: &Model) -> Node<Msg> {
|
||||||
let project_icon = Node::from_html(include_str!("../../static/project-avatar.svg"));
|
let project_icon = Node::from_html(include_str!("../../static/project-avatar.svg"));
|
||||||
@ -36,7 +50,7 @@ pub fn render(model: &Model) -> Node<Msg> {
|
|||||||
sidebar_link_item(model, "Components", Icon::Component, None),
|
sidebar_link_item(model, "Components", Icon::Component, None),
|
||||||
];
|
];
|
||||||
|
|
||||||
if model.user.as_ref().map(|u| u.user_role).unwrap_or_default() > UserRole::User {
|
if model.current_user_role() > UserRole::User {
|
||||||
links.push(sidebar_link_item(
|
links.push(sidebar_link_item(
|
||||||
model,
|
model,
|
||||||
"Users",
|
"Users",
|
||||||
|
@ -5,7 +5,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use jirs_data::WsMsg;
|
use jirs_data::WsMsg;
|
||||||
|
|
||||||
use crate::model::{Page, PageContent, SignInPage};
|
use crate::model::{Model, Page, PageContent, SignInPage};
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_field::StyledField;
|
use crate::shared::styled_field::StyledField;
|
||||||
use crate::shared::styled_form::StyledForm;
|
use crate::shared::styled_form::StyledForm;
|
||||||
@ -24,7 +24,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Msg::ChangePage(Page::SignIn) => {
|
Msg::ChangePage(Page::SignIn) => {
|
||||||
model.page_content = PageContent::SignIn(Box::new(SignInPage::default()));
|
build_page_content(model);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -85,6 +85,10 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_page_content(model: &mut Model) {
|
||||||
|
model.page_content = PageContent::SignIn(Box::new(SignInPage::default()));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view(model: &model::Model) -> Node<Msg> {
|
pub fn view(model: &model::Model) -> Node<Msg> {
|
||||||
let page = match &model.page_content {
|
let page = match &model.page_content {
|
||||||
PageContent::SignIn(page) => page,
|
PageContent::SignIn(page) => page,
|
||||||
|
@ -2,7 +2,7 @@ use seed::{prelude::*, *};
|
|||||||
|
|
||||||
use jirs_data::{SignUpFieldId, WsMsg};
|
use jirs_data::{SignUpFieldId, WsMsg};
|
||||||
|
|
||||||
use crate::model::{Page, PageContent, SignUpPage};
|
use crate::model::{Model, Page, PageContent, SignUpPage};
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_field::StyledField;
|
use crate::shared::styled_field::StyledField;
|
||||||
use crate::shared::styled_form::StyledForm;
|
use crate::shared::styled_form::StyledForm;
|
||||||
@ -21,7 +21,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Msg::ChangePage(Page::SignUp) => {
|
Msg::ChangePage(Page::SignUp) => {
|
||||||
model.page_content = PageContent::SignUp(Box::new(SignUpPage::default()));
|
build_page_content(model);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -61,6 +61,10 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_page_content(model: &mut Model) {
|
||||||
|
model.page_content = PageContent::SignUp(Box::new(SignUpPage::default()));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view(model: &model::Model) -> Node<Msg> {
|
pub fn view(model: &model::Model) -> Node<Msg> {
|
||||||
let page = match &model.page_content {
|
let page = match &model.page_content {
|
||||||
PageContent::SignUp(page) => page,
|
PageContent::SignUp(page) => page,
|
||||||
|
@ -231,11 +231,12 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.on_click(mouse_ev(Ev::Click, move |_| Msg::InvitedUserRemove(email)))
|
.on_click(mouse_ev(Ev::Click, move |_| Msg::InvitedUserRemove(email)))
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
|
// span![format!("{}", user.user_role)],
|
||||||
li![
|
li![
|
||||||
class!["user"],
|
class!["user"],
|
||||||
span![user.name.as_str()],
|
span![user.name.as_str()],
|
||||||
span![user.email.as_str()],
|
span![user.email.as_str()],
|
||||||
span![format!("{}", user.user_role)],
|
|
||||||
remove,
|
remove,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -90,6 +90,33 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
WsMsg::ProjectLoaded(project) => {
|
WsMsg::ProjectLoaded(project) => {
|
||||||
model.project = Some(project.clone());
|
model.project = Some(project.clone());
|
||||||
}
|
}
|
||||||
|
WsMsg::ProjectsLoaded(v) => {
|
||||||
|
model.projects = v.clone();
|
||||||
|
if !model.projects.is_empty() {
|
||||||
|
model.project = model.current_user_project.as_ref().and_then(|up| {
|
||||||
|
model
|
||||||
|
.projects
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.id == up.project_id)
|
||||||
|
.cloned()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// user projects
|
||||||
|
WsMsg::UserProjectLoaded(v) => {
|
||||||
|
model.user_projects = v.clone();
|
||||||
|
model.current_user_project = v.iter().find(|up| up.is_current).cloned();
|
||||||
|
if !model.projects.is_empty() {
|
||||||
|
model.project = model.current_user_project.as_ref().and_then(|up| {
|
||||||
|
model
|
||||||
|
.projects
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.id == up.project_id)
|
||||||
|
.cloned()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// issues
|
// issues
|
||||||
WsMsg::ProjectIssuesLoaded(v) => {
|
WsMsg::ProjectIssuesLoaded(v) => {
|
||||||
let mut v = v.clone();
|
let mut v = v.clone();
|
||||||
|
@ -21,6 +21,7 @@ pub trait ToVec {
|
|||||||
pub type IssueId = i32;
|
pub type IssueId = i32;
|
||||||
pub type ProjectId = i32;
|
pub type ProjectId = i32;
|
||||||
pub type UserId = i32;
|
pub type UserId = i32;
|
||||||
|
pub type UserProjectId = i32;
|
||||||
pub type CommentId = i32;
|
pub type CommentId = i32;
|
||||||
pub type TokenId = i32;
|
pub type TokenId = i32;
|
||||||
pub type IssueStatusId = i32;
|
pub type IssueStatusId = i32;
|
||||||
@ -468,10 +469,21 @@ pub struct User {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub avatar_url: Option<String>,
|
pub avatar_url: Option<String>,
|
||||||
pub project_id: ProjectId,
|
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
pub user_role: UserRole,
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "backend", derive(Queryable))]
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct UserProject {
|
||||||
|
pub id: UserProjectId,
|
||||||
|
pub user_id: UserId,
|
||||||
|
pub project_id: ProjectId,
|
||||||
|
pub is_default: bool,
|
||||||
|
pub is_current: bool,
|
||||||
|
pub role: UserRole,
|
||||||
|
pub created_at: NaiveDateTime,
|
||||||
|
pub updated_at: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "backend", derive(Queryable))]
|
#[cfg_attr(feature = "backend", derive(Queryable))]
|
||||||
@ -533,6 +545,7 @@ impl From<Issue> for UpdateIssuePayload {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "backend", derive(Queryable))]
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub id: MessageId,
|
pub id: MessageId,
|
||||||
@ -656,6 +669,7 @@ pub enum IssueFieldId {
|
|||||||
pub enum WsMsg {
|
pub enum WsMsg {
|
||||||
Ping,
|
Ping,
|
||||||
Pong,
|
Pong,
|
||||||
|
Die,
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
AuthorizeRequest(Uuid),
|
AuthorizeRequest(Uuid),
|
||||||
@ -697,6 +711,9 @@ pub enum WsMsg {
|
|||||||
// project page
|
// project page
|
||||||
ProjectRequest,
|
ProjectRequest,
|
||||||
ProjectLoaded(Project),
|
ProjectLoaded(Project),
|
||||||
|
ProjectsLoad,
|
||||||
|
ProjectsLoaded(Vec<Project>),
|
||||||
|
|
||||||
ProjectIssuesRequest,
|
ProjectIssuesRequest,
|
||||||
ProjectIssuesLoaded(Vec<Issue>),
|
ProjectIssuesLoaded(Vec<Issue>),
|
||||||
ProjectUsersRequest,
|
ProjectUsersRequest,
|
||||||
@ -734,6 +751,12 @@ pub enum WsMsg {
|
|||||||
ProfileUpdate(EmailString, UsernameString),
|
ProfileUpdate(EmailString, UsernameString),
|
||||||
ProfileUpdated,
|
ProfileUpdated,
|
||||||
|
|
||||||
|
// user projects
|
||||||
|
UserProjectLoad,
|
||||||
|
UserProjectLoaded(Vec<UserProject>),
|
||||||
|
UserProjectSetCurrent(UserProjectId),
|
||||||
|
UserProjectCurrentChanged(UserProject),
|
||||||
|
|
||||||
// messages
|
// messages
|
||||||
Message(Message),
|
Message(Message),
|
||||||
MessagesRequest,
|
MessagesRequest,
|
||||||
|
@ -8,16 +8,6 @@ repository = "https://gitlab.com/adrian.wozniak/jirs"
|
|||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
#license-file = "../LICENSE"
|
#license-file = "../LICENSE"
|
||||||
|
|
||||||
[target.x86_64-unknown-linux-gnu]
|
|
||||||
rustflags = [
|
|
||||||
"-C", "link-arg=-fuse-ld=lld",
|
|
||||||
]
|
|
||||||
|
|
||||||
[target.nightly-x86_64-unknown-linux-gnu]
|
|
||||||
rustflags = [
|
|
||||||
"-C", "link-arg=-fuse-ld=lld",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "jirs_server"
|
name = "jirs_server"
|
||||||
path = "./src/main.rs"
|
path = "./src/main.rs"
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE users ADD COLUMN role "UserRoleType" DEFAULT 'user' NOT NULL;
|
||||||
|
ALTER TABLE users ADD COLUMN project_id int;
|
||||||
|
|
||||||
|
UPDATE users
|
||||||
|
SET project_id = user_projects.project_id,
|
||||||
|
role = user_projects.role
|
||||||
|
FROM user_projects
|
||||||
|
INNER JOIN user_projects
|
||||||
|
ON user_projects.user_id = users.id;
|
||||||
|
|
||||||
|
DROP TABLE user_projects;
|
||||||
|
|
||||||
|
ALTER TABLE users
|
||||||
|
ALTER COLUMN project_id
|
||||||
|
ADD CONSTRAINT users_project_id_fkey
|
||||||
|
FOREIGN KEY (project_id)
|
||||||
|
REFERENCES projects (id)
|
||||||
|
MATCH FULL;
|
||||||
|
|
||||||
|
COMMIT;
|
@ -0,0 +1,22 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS user_projects CASCADE;
|
||||||
|
CREATE TABLE user_projects (
|
||||||
|
id serial primary key not null,
|
||||||
|
user_id int not null references users (id),
|
||||||
|
project_id int not null references projects (id),
|
||||||
|
is_default bool not null default false,
|
||||||
|
is_current bool not null default false,
|
||||||
|
role "UserRoleType" not null default 'user',
|
||||||
|
created_at timestamp not null default now(),
|
||||||
|
updated_at timestamp not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO user_projects (user_id, project_id, role, is_default, is_current)
|
||||||
|
SELECT id, project_id, role, true, true
|
||||||
|
FROM users;
|
||||||
|
|
||||||
|
ALTER TABLE users DROP COLUMN role;
|
||||||
|
ALTER TABLE users DROP COLUMN project_id;
|
||||||
|
|
||||||
|
COMMIT;
|
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS messages;
|
@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE messages (
|
||||||
|
id serial primary key not null,
|
||||||
|
receiver_id int not null references users (id),
|
||||||
|
sender_id int not null references users (id),
|
||||||
|
summary text not null,
|
||||||
|
description text not null,
|
||||||
|
message_type text not null,
|
||||||
|
hyper_link text not null,
|
||||||
|
created_at timestamp not null default now(),
|
||||||
|
updated_at timestamp not null default now()
|
||||||
|
);
|
@ -3,23 +3,27 @@ insert into projects (name) values ('initial'), ('second'), ('third');
|
|||||||
insert into issue_statuses (name, project_id, position)
|
insert into issue_statuses (name, project_id, position)
|
||||||
values ('backlog', 1, 1), ('selected', 1, 2), ('in_progress', 1, 3), ('done', 1, 4);
|
values ('backlog', 1, 1), ('selected', 1, 2), ('in_progress', 1, 3), ('done', 1, 4);
|
||||||
|
|
||||||
insert into users (project_id, email, name, avatar_url) values (
|
insert into users (email, name, avatar_url) values (
|
||||||
1,
|
|
||||||
'john@example.com',
|
'john@example.com',
|
||||||
'John Doe',
|
'John Doe',
|
||||||
'http://cdn.onlinewebfonts.com/svg/img_553934.png'
|
'http://cdn.onlinewebfonts.com/svg/img_553934.png'
|
||||||
), (
|
), (
|
||||||
1,
|
|
||||||
'kate@exampe.com',
|
'kate@exampe.com',
|
||||||
'Kate Snow',
|
'Kate Snow',
|
||||||
'http://www.asthmamd.org/images/icon_user_6.png'
|
'http://www.asthmamd.org/images/icon_user_6.png'
|
||||||
), (
|
), (
|
||||||
1,
|
|
||||||
'mike@example.com',
|
'mike@example.com',
|
||||||
'Mike Keningham',
|
'Mike Keningham',
|
||||||
'https://cdn0.iconfinder.com/data/icons/user-pictures/100/matureman1-512.png'
|
'https://cdn0.iconfinder.com/data/icons/user-pictures/100/matureman1-512.png'
|
||||||
);
|
);
|
||||||
insert into invitations ( email, name, state, project_id, invited_by_id) values (
|
insert into user_projects (user_id, project_id, role, is_current, is_default) values (
|
||||||
|
1, 1, 'owner', true, true
|
||||||
|
), (
|
||||||
|
2, 1, 'owner', true, true
|
||||||
|
), (
|
||||||
|
3, 1, 'owner', true, true
|
||||||
|
);
|
||||||
|
insert into invitations (email, name, state, project_id, invited_by_id) values (
|
||||||
'foo1@example.com',
|
'foo1@example.com',
|
||||||
'Foo1',
|
'Foo1',
|
||||||
'sent',
|
'sent',
|
||||||
|
@ -8,7 +8,7 @@ use jirs_data::{
|
|||||||
|
|
||||||
use crate::db::DbExecutor;
|
use crate::db::DbExecutor;
|
||||||
use crate::errors::ServiceErrors;
|
use crate::errors::ServiceErrors;
|
||||||
use crate::models::{InvitationForm, UserForm};
|
use crate::models::InvitationForm;
|
||||||
|
|
||||||
pub struct ListInvitation {
|
pub struct ListInvitation {
|
||||||
pub user_id: UserId,
|
pub user_id: UserId,
|
||||||
@ -150,7 +150,6 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
|||||||
|
|
||||||
fn handle(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
use crate::schema::invitations::dsl::*;
|
use crate::schema::invitations::dsl::*;
|
||||||
use crate::schema::users::dsl::users;
|
|
||||||
|
|
||||||
let conn = &self
|
let conn = &self
|
||||||
.pool
|
.pool
|
||||||
@ -181,17 +180,26 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
|||||||
.execute(conn)
|
.execute(conn)
|
||||||
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
|
|
||||||
let form = UserForm {
|
let user: User = {
|
||||||
name: invitation.name,
|
use crate::schema::users::dsl::*;
|
||||||
email: invitation.email,
|
|
||||||
avatar_url: None,
|
let query = diesel::insert_into(users)
|
||||||
project_id: invitation.project_id,
|
.values((name.eq(invitation.name), email.eq(invitation.email)));
|
||||||
};
|
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||||
let query = diesel::insert_into(users).values(form);
|
query
|
||||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
|
||||||
let user: User = query
|
|
||||||
.get_result(conn)
|
.get_result(conn)
|
||||||
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?
|
||||||
|
};
|
||||||
|
{
|
||||||
|
use crate::schema::user_projects::dsl::*;
|
||||||
|
|
||||||
|
let query = diesel::insert_into(user_projects)
|
||||||
|
.values((user_id.eq(user.id), project_id.eq(invitation.project_id)));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||||
|
query
|
||||||
|
.execute(conn)
|
||||||
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
|
};
|
||||||
|
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ pub mod issue_statuses;
|
|||||||
pub mod issues;
|
pub mod issues;
|
||||||
pub mod projects;
|
pub mod projects;
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
|
pub mod user_projects;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
@ -3,10 +3,11 @@ use diesel::pg::Pg;
|
|||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use jirs_data::{Project, ProjectCategory, TimeTracking};
|
use jirs_data::{Project, ProjectCategory, TimeTracking, UserId};
|
||||||
|
|
||||||
use crate::db::DbExecutor;
|
use crate::db::DbExecutor;
|
||||||
use crate::errors::ServiceErrors;
|
use crate::errors::ServiceErrors;
|
||||||
|
use crate::schema::projects::all_columns;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct LoadCurrentProject {
|
pub struct LoadCurrentProject {
|
||||||
@ -83,3 +84,34 @@ impl Handler<UpdateProject> for DbExecutor {
|
|||||||
.map_err(|_| ServiceErrors::RecordNotFound("Project".to_string()))
|
.map_err(|_| ServiceErrors::RecordNotFound("Project".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct LoadProjects {
|
||||||
|
pub user_id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for LoadProjects {
|
||||||
|
type Result = Result<Vec<Project>, ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<LoadProjects> for DbExecutor {
|
||||||
|
type Result = Result<Vec<Project>, ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: LoadProjects, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::projects::dsl::*;
|
||||||
|
use crate::schema::user_projects::dsl::{project_id, user_id, user_projects};
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
|
let query = projects
|
||||||
|
.inner_join(user_projects.on(project_id.eq(id)))
|
||||||
|
.filter(user_id.eq(msg.user_id))
|
||||||
|
.select(all_columns);
|
||||||
|
debug!("{}", diesel::debug_query::<diesel::pg::Pg, _>(&query));
|
||||||
|
query
|
||||||
|
.load::<Project>(conn)
|
||||||
|
.map_err(|_| ServiceErrors::RecordNotFound("Project".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
111
jirs-server/src/db/user_projects.rs
Normal file
111
jirs-server/src/db/user_projects.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
use actix::{Handler, Message};
|
||||||
|
use diesel::pg::Pg;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
use jirs_data::{UserId, UserProject, UserProjectId};
|
||||||
|
|
||||||
|
use crate::db::DbExecutor;
|
||||||
|
use crate::errors::ServiceErrors;
|
||||||
|
|
||||||
|
pub struct CurrentUserProject {
|
||||||
|
pub user_id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for CurrentUserProject {
|
||||||
|
type Result = Result<UserProject, ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<CurrentUserProject> for DbExecutor {
|
||||||
|
type Result = Result<UserProject, ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: CurrentUserProject, _: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::user_projects::dsl::*;
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
|
let user_query = user_projects.filter(user_id.eq(msg.user_id).and(is_current.eq(true)));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&user_query));
|
||||||
|
user_query
|
||||||
|
.first(conn)
|
||||||
|
.map_err(|_e| ServiceErrors::RecordNotFound(format!("user project {}", msg.user_id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LoadUserProjects {
|
||||||
|
pub user_id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for LoadUserProjects {
|
||||||
|
type Result = Result<Vec<UserProject>, ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<LoadUserProjects> for DbExecutor {
|
||||||
|
type Result = Result<Vec<UserProject>, ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: LoadUserProjects, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::user_projects::dsl::*;
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
|
let user_query = user_projects.filter(user_id.eq(msg.user_id));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&user_query));
|
||||||
|
user_query
|
||||||
|
.load(conn)
|
||||||
|
.map_err(|_e| ServiceErrors::RecordNotFound(format!("user project {}", msg.user_id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChangeCurrentUserProject {
|
||||||
|
pub user_id: UserId,
|
||||||
|
pub id: UserProjectId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for ChangeCurrentUserProject {
|
||||||
|
type Result = Result<UserProject, ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<ChangeCurrentUserProject> for DbExecutor {
|
||||||
|
type Result = Result<UserProject, ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: ChangeCurrentUserProject, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::user_projects::dsl::*;
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
|
let query = user_projects.filter(id.eq(msg.id).and(user_id.eq(msg.user_id)));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||||
|
let mut user_project: UserProject = query
|
||||||
|
.first(conn)
|
||||||
|
.map_err(|_e| ServiceErrors::RecordNotFound(format!("user project {}", msg.user_id)))?;
|
||||||
|
|
||||||
|
let query = diesel::update(user_projects)
|
||||||
|
.set(is_current.eq(false))
|
||||||
|
.filter(user_id.eq(msg.user_id));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||||
|
query
|
||||||
|
.execute(conn)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_e| ServiceErrors::RecordNotFound(format!("user project {}", msg.user_id)))?;
|
||||||
|
|
||||||
|
let query = diesel::update(user_projects)
|
||||||
|
.set(is_current.eq(true))
|
||||||
|
.filter(id.eq(msg.id).and(user_id.eq(msg.user_id)));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||||
|
query
|
||||||
|
.execute(conn)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_e| ServiceErrors::RecordNotFound(format!("user project {}", msg.user_id)))?;
|
||||||
|
|
||||||
|
user_project.is_current = true;
|
||||||
|
Ok(user_project)
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,8 @@ use jirs_data::{Project, User, UserId};
|
|||||||
|
|
||||||
use crate::db::{DbExecutor, DbPooledConn};
|
use crate::db::{DbExecutor, DbPooledConn};
|
||||||
use crate::errors::ServiceErrors;
|
use crate::errors::ServiceErrors;
|
||||||
use crate::models::{CreateProjectForm, UserForm};
|
use crate::models::CreateProjectForm;
|
||||||
|
use crate::schema::users::all_columns;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct FindUser {
|
pub struct FindUser {
|
||||||
@ -54,6 +55,7 @@ impl Handler<LoadProjectUsers> for DbExecutor {
|
|||||||
type Result = Result<Vec<User>, ServiceErrors>;
|
type Result = Result<Vec<User>, ServiceErrors>;
|
||||||
|
|
||||||
fn handle(&mut self, msg: LoadProjectUsers, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: LoadProjectUsers, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::user_projects::dsl::{project_id, user_id, user_projects};
|
||||||
use crate::schema::users::dsl::*;
|
use crate::schema::users::dsl::*;
|
||||||
|
|
||||||
let conn = &self
|
let conn = &self
|
||||||
@ -61,7 +63,11 @@ impl Handler<LoadProjectUsers> for DbExecutor {
|
|||||||
.get()
|
.get()
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
let users_query = users.distinct_on(id).filter(project_id.eq(msg.project_id));
|
let users_query = users
|
||||||
|
.distinct_on(id)
|
||||||
|
.inner_join(user_projects.on(user_id.eq(id)))
|
||||||
|
.filter(project_id.eq(msg.project_id))
|
||||||
|
.select(all_columns);
|
||||||
debug!("{}", diesel::debug_query::<Pg, _>(&users_query));
|
debug!("{}", diesel::debug_query::<Pg, _>(&users_query));
|
||||||
users_query
|
users_query
|
||||||
.load(conn)
|
.load(conn)
|
||||||
@ -142,19 +148,31 @@ impl Handler<Register> for DbExecutor {
|
|||||||
.get_result(conn)
|
.get_result(conn)
|
||||||
.map_err(|_| ServiceErrors::RegisterCollision)?;
|
.map_err(|_| ServiceErrors::RegisterCollision)?;
|
||||||
|
|
||||||
let form = UserForm {
|
let user: User = {
|
||||||
name: msg.name,
|
let insert_user_query =
|
||||||
email: msg.email,
|
diesel::insert_into(users).values((name.eq(msg.name), email.eq(msg.email)));
|
||||||
avatar_url: None,
|
debug!("{}", diesel::debug_query::<Pg, _>(&insert_user_query));
|
||||||
project_id: project.id,
|
insert_user_query
|
||||||
|
.get_result(conn)
|
||||||
|
.map_err(|_| ServiceErrors::RegisterCollision)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let insert_user_query = diesel::insert_into(users).values(form);
|
{
|
||||||
debug!("{}", diesel::debug_query::<Pg, _>(&insert_user_query));
|
use crate::schema::user_projects::dsl::*;
|
||||||
match insert_user_query.execute(conn) {
|
let insert_user_project_query = diesel::insert_into(user_projects).values((
|
||||||
Ok(_) => (),
|
user_id.eq(user.id),
|
||||||
_ => return Err(ServiceErrors::RegisterCollision),
|
project_id.eq(project.id),
|
||||||
};
|
is_current.eq(true),
|
||||||
|
is_default.eq(true),
|
||||||
|
));
|
||||||
|
debug!(
|
||||||
|
"{}",
|
||||||
|
diesel::debug_query::<Pg, _>(&insert_user_project_query)
|
||||||
|
);
|
||||||
|
insert_user_project_query
|
||||||
|
.execute(conn)
|
||||||
|
.map_err(|_| ServiceErrors::RegisterCollision)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -287,11 +305,13 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_collision() {
|
fn check_collision() {
|
||||||
use crate::schema::projects::dsl::projects;
|
use crate::schema::projects::dsl::projects;
|
||||||
|
use crate::schema::user_projects::dsl::user_projects;
|
||||||
use crate::schema::users::dsl::users;
|
use crate::schema::users::dsl::users;
|
||||||
|
|
||||||
let pool = build_pool();
|
let pool = build_pool();
|
||||||
let conn = &pool.get().unwrap();
|
let conn = &pool.get().unwrap();
|
||||||
|
|
||||||
|
diesel::delete(user_projects).execute(conn).unwrap();
|
||||||
diesel::delete(users).execute(conn).unwrap();
|
diesel::delete(users).execute(conn).unwrap();
|
||||||
diesel::delete(projects).execute(conn).unwrap();
|
diesel::delete(projects).execute(conn).unwrap();
|
||||||
|
|
||||||
@ -306,16 +326,28 @@ mod tests {
|
|||||||
.get_result(conn)
|
.get_result(conn)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let user_form = UserForm {
|
let user: User = {
|
||||||
name: "Foo".to_string(),
|
use crate::schema::users::dsl::*;
|
||||||
email: "foo@example.com".to_string(),
|
|
||||||
avatar_url: None,
|
|
||||||
project_id: project.id,
|
|
||||||
};
|
|
||||||
diesel::insert_into(users)
|
diesel::insert_into(users)
|
||||||
.values(user_form)
|
.values((
|
||||||
|
name.eq("Foo".to_string()),
|
||||||
|
email.eq("foo@example.com".to_string()),
|
||||||
|
))
|
||||||
|
.get_result(conn)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
{
|
||||||
|
use crate::schema::user_projects::dsl::*;
|
||||||
|
diesel::insert_into(user_projects)
|
||||||
|
.values((
|
||||||
|
user_id.eq(user.id),
|
||||||
|
project_id.eq(project.id),
|
||||||
|
is_current.eq(true),
|
||||||
|
is_default.eq(true),
|
||||||
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(count_matching_users("Foo", "bar@example.com", conn), 1);
|
assert_eq!(count_matching_users("Foo", "bar@example.com", conn), 1);
|
||||||
assert_eq!(count_matching_users("Bar", "foo@example.com", conn), 1);
|
assert_eq!(count_matching_users("Bar", "foo@example.com", conn), 1);
|
||||||
|
@ -109,7 +109,6 @@ pub struct UserForm {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub avatar_url: Option<String>,
|
pub avatar_url: Option<String>,
|
||||||
pub project_id: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Insertable)]
|
#[derive(Debug, Serialize, Deserialize, Insertable)]
|
||||||
|
@ -301,6 +301,71 @@ table! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `messages` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
messages (id) {
|
||||||
|
/// The `id` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
id -> Int4,
|
||||||
|
/// The `receiver_id` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
receiver_id -> Int4,
|
||||||
|
/// The `sender_id` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
sender_id -> Int4,
|
||||||
|
/// The `summary` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
summary -> Text,
|
||||||
|
/// The `description` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
description -> Text,
|
||||||
|
/// The `message_type` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
message_type -> Text,
|
||||||
|
/// The `hyper_link` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
hyper_link -> Text,
|
||||||
|
/// The `created_at` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `messages` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
use diesel::sql_types::*;
|
use diesel::sql_types::*;
|
||||||
use jirs_data::sql::*;
|
use jirs_data::sql::*;
|
||||||
@ -413,6 +478,65 @@ table! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
user_projects (id) {
|
||||||
|
/// The `id` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
id -> Int4,
|
||||||
|
/// The `user_id` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
user_id -> Int4,
|
||||||
|
/// The `project_id` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
project_id -> Int4,
|
||||||
|
/// The `is_default` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Bool`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
is_default -> Bool,
|
||||||
|
/// The `is_current` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Bool`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
is_current -> Bool,
|
||||||
|
/// The `role` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `UserRoleType`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
role -> UserRoleType,
|
||||||
|
/// The `created_at` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `user_projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
use diesel::sql_types::*;
|
use diesel::sql_types::*;
|
||||||
use jirs_data::sql::*;
|
use jirs_data::sql::*;
|
||||||
@ -445,12 +569,6 @@ table! {
|
|||||||
///
|
///
|
||||||
/// (Automatically generated by Diesel.)
|
/// (Automatically generated by Diesel.)
|
||||||
avatar_url -> Nullable<Text>,
|
avatar_url -> Nullable<Text>,
|
||||||
/// The `project_id` column of the `users` table.
|
|
||||||
///
|
|
||||||
/// Its SQL type is `Int4`.
|
|
||||||
///
|
|
||||||
/// (Automatically generated by Diesel.)
|
|
||||||
project_id -> Int4,
|
|
||||||
/// The `created_at` column of the `users` table.
|
/// The `created_at` column of the `users` table.
|
||||||
///
|
///
|
||||||
/// Its SQL type is `Timestamp`.
|
/// Its SQL type is `Timestamp`.
|
||||||
@ -463,12 +581,6 @@ table! {
|
|||||||
///
|
///
|
||||||
/// (Automatically generated by Diesel.)
|
/// (Automatically generated by Diesel.)
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
/// The `role` column of the `users` table.
|
|
||||||
///
|
|
||||||
/// Its SQL type is `UserRoleType`.
|
|
||||||
///
|
|
||||||
/// (Automatically generated by Diesel.)
|
|
||||||
role -> UserRoleType,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,7 +595,8 @@ joinable!(issues -> issue_statuses (issue_status_id));
|
|||||||
joinable!(issues -> projects (project_id));
|
joinable!(issues -> projects (project_id));
|
||||||
joinable!(issues -> users (reporter_id));
|
joinable!(issues -> users (reporter_id));
|
||||||
joinable!(tokens -> users (user_id));
|
joinable!(tokens -> users (user_id));
|
||||||
joinable!(users -> projects (project_id));
|
joinable!(user_projects -> projects (project_id));
|
||||||
|
joinable!(user_projects -> users (user_id));
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
allow_tables_to_appear_in_same_query!(
|
||||||
comments,
|
comments,
|
||||||
@ -491,7 +604,9 @@ allow_tables_to_appear_in_same_query!(
|
|||||||
issue_assignees,
|
issue_assignees,
|
||||||
issues,
|
issues,
|
||||||
issue_statuses,
|
issue_statuses,
|
||||||
|
messages,
|
||||||
projects,
|
projects,
|
||||||
tokens,
|
tokens,
|
||||||
|
user_projects,
|
||||||
users,
|
users,
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ use actix_multipart::{Field, Multipart};
|
|||||||
use actix_web::http::header::ContentDisposition;
|
use actix_web::http::header::ContentDisposition;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_web::{post, web, Error, HttpResponse};
|
use actix_web::{post, web, Error, HttpResponse};
|
||||||
|
use futures::executor::block_on;
|
||||||
use futures::{StreamExt, TryStreamExt};
|
use futures::{StreamExt, TryStreamExt};
|
||||||
#[cfg(feature = "aws-s3")]
|
#[cfg(feature = "aws-s3")]
|
||||||
use rusoto_s3::{PutObjectRequest, S3Client, S3};
|
use rusoto_s3::{PutObjectRequest, S3Client, S3};
|
||||||
@ -16,6 +17,7 @@ use rusoto_s3::{PutObjectRequest, S3Client, S3};
|
|||||||
use jirs_data::{User, UserId, WsMsg};
|
use jirs_data::{User, UserId, WsMsg};
|
||||||
|
|
||||||
use crate::db::authorize_user::AuthorizeUser;
|
use crate::db::authorize_user::AuthorizeUser;
|
||||||
|
use crate::db::user_projects::CurrentUserProject;
|
||||||
use crate::db::users::UpdateAvatarUrl;
|
use crate::db::users::UpdateAvatarUrl;
|
||||||
use crate::db::DbExecutor;
|
use crate::db::DbExecutor;
|
||||||
#[cfg(feature = "aws-s3")]
|
#[cfg(feature = "aws-s3")]
|
||||||
@ -51,11 +53,21 @@ pub async fn upload(
|
|||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let user_id = match user_id {
|
||||||
|
Some(id) => id,
|
||||||
|
_ => return Ok(HttpResponse::Unauthorized().finish()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let project_id = match block_on(db.send(CurrentUserProject { user_id })) {
|
||||||
|
Ok(Ok(user_project)) => user_project.project_id,
|
||||||
|
_ => return Ok(HttpResponse::UnprocessableEntity().finish()),
|
||||||
|
};
|
||||||
|
|
||||||
match (user_id, avatar_url) {
|
match (user_id, avatar_url) {
|
||||||
(Some(user_id), Some(avatar_url)) => {
|
(user_id, Some(avatar_url)) => {
|
||||||
let user = update_user_avatar(user_id, avatar_url.clone(), db).await?;
|
let user = update_user_avatar(user_id, avatar_url.clone(), db).await?;
|
||||||
ws.send(BroadcastToChannel(
|
ws.send(BroadcastToChannel(
|
||||||
user.project_id,
|
project_id,
|
||||||
WsMsg::AvatarUrlChanged(user.id, avatar_url),
|
WsMsg::AvatarUrlChanged(user.id, avatar_url),
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
|
@ -78,6 +78,9 @@ impl WsHandler<CheckAuthToken> for WebSocketActor {
|
|||||||
_ => return Ok(Some(WsMsg::AuthorizeExpired)),
|
_ => return Ok(Some(WsMsg::AuthorizeExpired)),
|
||||||
};
|
};
|
||||||
self.current_user = Some(user.clone());
|
self.current_user = Some(user.clone());
|
||||||
|
self.current_user_project = self.load_user_project().ok();
|
||||||
|
self.current_project = self.load_project().ok();
|
||||||
|
|
||||||
block_on(self.join_channel(ctx.address().recipient()));
|
block_on(self.join_channel(ctx.address().recipient()));
|
||||||
Ok(Some(WsMsg::AuthorizeLoaded(Ok(user))))
|
Ok(Some(WsMsg::AuthorizeLoaded(Ok(user))))
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,14 @@ impl WsHandler<LoadIssueComments> for WebSocketActor {
|
|||||||
issue_id: msg.issue_id,
|
issue_id: msg.issue_id,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(comments)) => comments,
|
Ok(Ok(comments)) => comments,
|
||||||
_ => return Ok(None),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(WsMsg::IssueCommentsLoaded(comments)))
|
Ok(Some(WsMsg::IssueCommentsLoaded(comments)))
|
||||||
@ -38,7 +45,14 @@ impl WsHandler<CreateCommentPayload> for WebSocketActor {
|
|||||||
body: msg.body,
|
body: msg.body,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(_)) => (),
|
Ok(Ok(_)) => (),
|
||||||
_ => return Ok(None),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
self.handle_msg(LoadIssueComments { issue_id }, ctx)
|
self.handle_msg(LoadIssueComments { issue_id }, ctx)
|
||||||
}
|
}
|
||||||
@ -62,7 +76,14 @@ impl WsHandler<UpdateCommentPayload> for WebSocketActor {
|
|||||||
body,
|
body,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(comment)) => comment.issue_id,
|
Ok(Ok(comment)) => comment.issue_id,
|
||||||
_ => return Ok(None),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if let Some(v) = self.handle_msg(LoadIssueComments { issue_id }, ctx)? {
|
if let Some(v) = self.handle_msg(LoadIssueComments { issue_id }, ctx)? {
|
||||||
self.broadcast(&v);
|
self.broadcast(&v);
|
||||||
@ -87,7 +108,14 @@ impl WsHandler<DeleteComment> for WebSocketActor {
|
|||||||
};
|
};
|
||||||
match block_on(self.db.send(m)) {
|
match block_on(self.db.send(m)) {
|
||||||
Ok(Ok(_)) => (),
|
Ok(Ok(_)) => (),
|
||||||
_ => return Ok(None),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(WsMsg::CommentDeleted(msg.comment_id)))
|
Ok(Some(WsMsg::CommentDeleted(msg.comment_id)))
|
||||||
|
@ -15,7 +15,14 @@ impl WsHandler<ListInvitation> for WebSocketActor {
|
|||||||
};
|
};
|
||||||
let res = match block_on(self.db.send(invitations::ListInvitation { user_id })) {
|
let res = match block_on(self.db.send(invitations::ListInvitation { user_id })) {
|
||||||
Ok(Ok(v)) => Some(WsMsg::InvitationListLoaded(v)),
|
Ok(Ok(v)) => Some(WsMsg::InvitationListLoaded(v)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@ -28,11 +35,12 @@ pub struct CreateInvitation {
|
|||||||
|
|
||||||
impl WsHandler<CreateInvitation> for WebSocketActor {
|
impl WsHandler<CreateInvitation> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, msg: CreateInvitation, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, msg: CreateInvitation, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let (user_id, inviter_name, project_id) = match self
|
let project_id = match self.current_user_project.as_ref() {
|
||||||
.current_user
|
Some(up) => up.project_id,
|
||||||
.as_ref()
|
_ => return Ok(None),
|
||||||
.map(|u| (u.id, u.name.clone(), u.project_id))
|
};
|
||||||
{
|
let (user_id, inviter_name) =
|
||||||
|
match self.current_user.as_ref().map(|u| (u.id, u.name.clone())) {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
};
|
};
|
||||||
@ -84,7 +92,14 @@ impl WsHandler<DeleteInvitation> for WebSocketActor {
|
|||||||
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 })) {
|
||||||
Ok(Ok(_)) => None,
|
Ok(Ok(_)) => None,
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@ -100,7 +115,14 @@ impl WsHandler<RevokeInvitation> for WebSocketActor {
|
|||||||
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 })) {
|
||||||
Ok(Ok(_)) => Some(WsMsg::InvitationRevokeSuccess(id)),
|
Ok(Ok(_)) => Some(WsMsg::InvitationRevokeSuccess(id)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@ -116,7 +138,14 @@ impl WsHandler<AcceptInvitation> for WebSocketActor {
|
|||||||
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 })) {
|
||||||
Ok(Ok(_)) => Some(WsMsg::InvitationAcceptSuccess(id)),
|
Ok(Ok(_)) => Some(WsMsg::InvitationAcceptSuccess(id)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,21 @@ pub struct LoadIssueStatuses;
|
|||||||
|
|
||||||
impl WsHandler<LoadIssueStatuses> for WebSocketActor {
|
impl WsHandler<LoadIssueStatuses> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, _msg: LoadIssueStatuses, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, _msg: LoadIssueStatuses, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
|
||||||
let msg = match block_on(
|
let msg = match block_on(
|
||||||
self.db
|
self.db
|
||||||
.send(issue_statuses::LoadIssueStatuses { project_id }),
|
.send(issue_statuses::LoadIssueStatuses { project_id }),
|
||||||
) {
|
) {
|
||||||
Ok(Ok(v)) => Some(WsMsg::IssueStatusesResponse(v)),
|
Ok(Ok(v)) => Some(WsMsg::IssueStatusesResponse(v)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
}
|
}
|
||||||
@ -29,7 +36,7 @@ pub struct CreateIssueStatus {
|
|||||||
|
|
||||||
impl WsHandler<CreateIssueStatus> for WebSocketActor {
|
impl WsHandler<CreateIssueStatus> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, msg: CreateIssueStatus, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, msg: CreateIssueStatus, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
|
||||||
let CreateIssueStatus { position, name } = msg;
|
let CreateIssueStatus { position, name } = msg;
|
||||||
let msg = match block_on(self.db.send(issue_statuses::CreateIssueStatus {
|
let msg = match block_on(self.db.send(issue_statuses::CreateIssueStatus {
|
||||||
@ -38,7 +45,14 @@ impl WsHandler<CreateIssueStatus> for WebSocketActor {
|
|||||||
name,
|
name,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(is)) => Some(WsMsg::IssueStatusCreated(is)),
|
Ok(Ok(is)) => Some(WsMsg::IssueStatusCreated(is)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
}
|
}
|
||||||
@ -50,7 +64,7 @@ pub struct DeleteIssueStatus {
|
|||||||
|
|
||||||
impl WsHandler<DeleteIssueStatus> for WebSocketActor {
|
impl WsHandler<DeleteIssueStatus> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, msg: DeleteIssueStatus, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, msg: DeleteIssueStatus, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
|
||||||
let DeleteIssueStatus { issue_status_id } = msg;
|
let DeleteIssueStatus { issue_status_id } = msg;
|
||||||
let msg = match block_on(self.db.send(issue_statuses::DeleteIssueStatus {
|
let msg = match block_on(self.db.send(issue_statuses::DeleteIssueStatus {
|
||||||
@ -58,7 +72,14 @@ impl WsHandler<DeleteIssueStatus> for WebSocketActor {
|
|||||||
project_id,
|
project_id,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(is)) => Some(WsMsg::IssueStatusDeleted(is)),
|
Ok(Ok(is)) => Some(WsMsg::IssueStatusDeleted(is)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
}
|
}
|
||||||
@ -72,7 +93,7 @@ pub struct UpdateIssueStatus {
|
|||||||
|
|
||||||
impl WsHandler<UpdateIssueStatus> for WebSocketActor {
|
impl WsHandler<UpdateIssueStatus> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, msg: UpdateIssueStatus, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, msg: UpdateIssueStatus, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
|
||||||
let UpdateIssueStatus {
|
let UpdateIssueStatus {
|
||||||
issue_status_id,
|
issue_status_id,
|
||||||
@ -86,7 +107,14 @@ impl WsHandler<UpdateIssueStatus> for WebSocketActor {
|
|||||||
project_id,
|
project_id,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(is)) => Some(WsMsg::IssueStatusUpdated(is)),
|
Ok(Ok(is)) => Some(WsMsg::IssueStatusUpdated(is)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if let Some(ws_msg) = msg.as_ref() {
|
if let Some(ws_msg) = msg.as_ref() {
|
||||||
self.broadcast(ws_msg)
|
self.broadcast(ws_msg)
|
||||||
|
@ -71,7 +71,14 @@ impl WsHandler<UpdateIssueHandler> for WebSocketActor {
|
|||||||
let assignees: Vec<IssueAssignee> =
|
let assignees: Vec<IssueAssignee> =
|
||||||
match block_on(self.db.send(LoadAssignees { issue_id: issue.id })) {
|
match block_on(self.db.send(LoadAssignees { issue_id: issue.id })) {
|
||||||
Ok(Ok(v)) => v,
|
Ok(Ok(v)) => v,
|
||||||
_ => return Ok(None),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for assignee in assignees {
|
for assignee in assignees {
|
||||||
@ -102,7 +109,14 @@ impl WsHandler<CreateIssuePayload> for WebSocketActor {
|
|||||||
};
|
};
|
||||||
let m = match block_on(self.db.send(msg)) {
|
let m = match block_on(self.db.send(msg)) {
|
||||||
Ok(Ok(issue)) => Some(WsMsg::IssueCreated(issue.into())),
|
Ok(Ok(issue)) => Some(WsMsg::IssueCreated(issue.into())),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(m)
|
Ok(m)
|
||||||
}
|
}
|
||||||
@ -120,7 +134,14 @@ impl WsHandler<DeleteIssue> for WebSocketActor {
|
|||||||
.send(crate::db::issues::DeleteIssue { issue_id: msg.id }),
|
.send(crate::db::issues::DeleteIssue { issue_id: msg.id }),
|
||||||
) {
|
) {
|
||||||
Ok(Ok(_)) => Some(WsMsg::IssueDeleted(msg.id)),
|
Ok(Ok(_)) => Some(WsMsg::IssueDeleted(msg.id)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(m)
|
Ok(m)
|
||||||
}
|
}
|
||||||
@ -130,7 +151,7 @@ pub struct LoadIssues;
|
|||||||
|
|
||||||
impl WsHandler<LoadIssues> for WebSocketActor {
|
impl WsHandler<LoadIssues> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, _msg: LoadIssues, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, _msg: LoadIssues, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
|
||||||
let issues: Vec<jirs_data::Issue> =
|
let issues: Vec<jirs_data::Issue> =
|
||||||
match block_on(self.db.send(LoadProjectIssues { project_id })) {
|
match block_on(self.db.send(LoadProjectIssues { project_id })) {
|
||||||
|
@ -6,9 +6,12 @@ use actix::{
|
|||||||
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 jirs_data::{ProjectId, UserId, WsMsg};
|
use jirs_data::{Project, ProjectId, User, UserId, UserProject, WsMsg};
|
||||||
|
|
||||||
|
use crate::db::projects::LoadCurrentProject;
|
||||||
|
use crate::db::user_projects::CurrentUserProject;
|
||||||
use crate::db::DbExecutor;
|
use crate::db::DbExecutor;
|
||||||
use crate::mail::MailExecutor;
|
use crate::mail::MailExecutor;
|
||||||
use crate::ws::auth::*;
|
use crate::ws::auth::*;
|
||||||
@ -25,6 +28,7 @@ pub mod invitations;
|
|||||||
pub mod issue_statuses;
|
pub mod issue_statuses;
|
||||||
pub mod issues;
|
pub mod issues;
|
||||||
pub mod projects;
|
pub mod projects;
|
||||||
|
pub mod user_projects;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|
||||||
pub type WsResult = std::result::Result<Option<WsMsg>, WsMsg>;
|
pub type WsResult = std::result::Result<Option<WsMsg>, WsMsg>;
|
||||||
@ -36,8 +40,10 @@ trait WsMessageSender {
|
|||||||
struct WebSocketActor {
|
struct WebSocketActor {
|
||||||
db: Data<Addr<DbExecutor>>,
|
db: Data<Addr<DbExecutor>>,
|
||||||
mail: Data<Addr<MailExecutor>>,
|
mail: Data<Addr<MailExecutor>>,
|
||||||
current_user: Option<jirs_data::User>,
|
|
||||||
addr: Addr<WsServer>,
|
addr: Addr<WsServer>,
|
||||||
|
current_user: Option<jirs_data::User>,
|
||||||
|
current_user_project: Option<jirs_data::UserProject>,
|
||||||
|
current_project: Option<jirs_data::Project>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for WebSocketActor {
|
impl Actor for WebSocketActor {
|
||||||
@ -62,12 +68,12 @@ impl Handler<InnerMsg> for WebSocketActor {
|
|||||||
|
|
||||||
impl WebSocketActor {
|
impl WebSocketActor {
|
||||||
fn broadcast(&self, msg: &WsMsg) {
|
fn broadcast(&self, msg: &WsMsg) {
|
||||||
let user = match self.current_user.as_ref() {
|
let project_id = match self.require_user_project() {
|
||||||
Some(u) => u,
|
Ok(up) => up.project_id,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
self.addr
|
self.addr
|
||||||
.do_send(InnerMsg::BroadcastToChannel(user.project_id, msg.clone()));
|
.do_send(InnerMsg::BroadcastToChannel(project_id, msg.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ws_msg(
|
fn handle_ws_msg(
|
||||||
@ -179,13 +185,18 @@ impl WebSocketActor {
|
|||||||
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);
|
||||||
|
|
||||||
let user = match self.current_user.as_ref() {
|
let user = match self.current_user.as_ref() {
|
||||||
None => return,
|
None => return,
|
||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
};
|
};
|
||||||
|
let project_id = match self.require_user_project() {
|
||||||
|
Ok(user_project) => user_project.project_id,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
match self
|
match self
|
||||||
.addr
|
.addr
|
||||||
.send(InnerMsg::Join(user.project_id, user.id, addr))
|
.send(InnerMsg::Join(project_id, user.id, addr))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(e) => error!("{}", e),
|
Err(e) => error!("{}", e),
|
||||||
@ -193,12 +204,42 @@ impl WebSocketActor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_user(&self) -> Result<&jirs_data::User, WsMsg> {
|
fn require_user(&self) -> Result<&User, WsMsg> {
|
||||||
self.current_user
|
self.current_user
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|u| u)
|
.map(|u| u)
|
||||||
.ok_or_else(|| WsMsg::AuthorizeExpired)
|
.ok_or_else(|| WsMsg::AuthorizeExpired)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn require_user_project(&self) -> Result<&UserProject, WsMsg> {
|
||||||
|
self.current_user_project
|
||||||
|
.as_ref()
|
||||||
|
.map(|u| u)
|
||||||
|
.ok_or_else(|| WsMsg::AuthorizeExpired)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn require_project(&self) -> Result<&Project, WsMsg> {
|
||||||
|
// self.current_project
|
||||||
|
// .as_ref()
|
||||||
|
// .map(|u| u)
|
||||||
|
// .ok_or_else(|| WsMsg::AuthorizeExpired)
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn load_user_project(&self) -> Result<UserProject, WsMsg> {
|
||||||
|
let user_id = self.require_user().map_err(|_| WsMsg::AuthorizeExpired)?.id;
|
||||||
|
match block_on(self.db.send(CurrentUserProject { user_id })) {
|
||||||
|
Ok(Ok(user_project)) => Ok(user_project),
|
||||||
|
_ => Err(WsMsg::AuthorizeExpired),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_project(&self) -> Result<Project, WsMsg> {
|
||||||
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
match block_on(self.db.send(LoadCurrentProject { project_id })) {
|
||||||
|
Ok(Ok(project)) => Ok(project),
|
||||||
|
_ => Err(WsMsg::AuthorizeExpired),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
||||||
@ -226,9 +267,12 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
|||||||
|
|
||||||
fn finished(&mut self, ctx: &mut Self::Context) {
|
fn finished(&mut self, ctx: &mut Self::Context) {
|
||||||
info!("Disconnected");
|
info!("Disconnected");
|
||||||
if let Some(user) = self.current_user.as_ref() {
|
if let (Some(user), Some(up)) = (
|
||||||
|
self.current_user.as_ref(),
|
||||||
|
self.current_user_project.as_ref(),
|
||||||
|
) {
|
||||||
self.addr.do_send(InnerMsg::Leave(
|
self.addr.do_send(InnerMsg::Leave(
|
||||||
user.project_id,
|
up.project_id,
|
||||||
user.id,
|
user.id,
|
||||||
ctx.address().recipient(),
|
ctx.address().recipient(),
|
||||||
));
|
));
|
||||||
@ -353,6 +397,8 @@ pub async fn index(
|
|||||||
db,
|
db,
|
||||||
mail,
|
mail,
|
||||||
current_user: None,
|
current_user: None,
|
||||||
|
current_user_project: None,
|
||||||
|
current_project: None,
|
||||||
addr: ws_server.get_ref().clone(),
|
addr: ws_server.get_ref().clone(),
|
||||||
},
|
},
|
||||||
&req,
|
&req,
|
||||||
|
@ -9,7 +9,7 @@ pub struct CurrentProject;
|
|||||||
|
|
||||||
impl WsHandler<CurrentProject> for WebSocketActor {
|
impl WsHandler<CurrentProject> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, _msg: CurrentProject, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, _msg: CurrentProject, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
|
||||||
let m = match block_on(self.db.send(LoadCurrentProject { project_id })) {
|
let m = match block_on(self.db.send(LoadCurrentProject { project_id })) {
|
||||||
Ok(Ok(project)) => Some(WsMsg::ProjectLoaded(project)),
|
Ok(Ok(project)) => Some(WsMsg::ProjectLoaded(project)),
|
||||||
@ -28,7 +28,7 @@ impl WsHandler<CurrentProject> for WebSocketActor {
|
|||||||
|
|
||||||
impl WsHandler<UpdateProjectPayload> for WebSocketActor {
|
impl WsHandler<UpdateProjectPayload> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, msg: UpdateProjectPayload, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, msg: UpdateProjectPayload, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
let project = match block_on(self.db.send(crate::db::projects::UpdateProject {
|
let project = match block_on(self.db.send(crate::db::projects::UpdateProject {
|
||||||
project_id,
|
project_id,
|
||||||
name: msg.name,
|
name: msg.name,
|
||||||
|
52
jirs-server/src/ws/user_projects.rs
Normal file
52
jirs-server/src/ws/user_projects.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use futures::executor::block_on;
|
||||||
|
|
||||||
|
use jirs_data::{UserProjectId, WsMsg};
|
||||||
|
|
||||||
|
use crate::db;
|
||||||
|
use crate::ws::{WebSocketActor, WsHandler, WsResult};
|
||||||
|
|
||||||
|
pub struct LoadUserProjects;
|
||||||
|
|
||||||
|
impl WsHandler<LoadUserProjects> for WebSocketActor {
|
||||||
|
fn handle_msg(&mut self, _msg: LoadUserProjects, _ctx: &mut Self::Context) -> WsResult {
|
||||||
|
let user_id = self.require_user()?.id;
|
||||||
|
match block_on(
|
||||||
|
self.db
|
||||||
|
.send(db::user_projects::LoadUserProjects { user_id }),
|
||||||
|
) {
|
||||||
|
Ok(Ok(v)) => Ok(Some(WsMsg::UserProjectLoaded(v))),
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SetCurrentUserProject {
|
||||||
|
pub id: UserProjectId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WsHandler<SetCurrentUserProject> for WebSocketActor {
|
||||||
|
fn handle_msg(&mut self, msg: SetCurrentUserProject, _ctx: &mut Self::Context) -> WsResult {
|
||||||
|
let user_id = self.require_user()?.id;
|
||||||
|
match block_on(self.db.send(db::user_projects::ChangeCurrentUserProject {
|
||||||
|
user_id,
|
||||||
|
id: msg.id,
|
||||||
|
})) {
|
||||||
|
Ok(Ok(user_project)) => Ok(Some(WsMsg::UserProjectCurrentChanged(user_project))),
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,10 +12,17 @@ impl WsHandler<LoadProjectUsers> for WebSocketActor {
|
|||||||
fn handle_msg(&mut self, _msg: LoadProjectUsers, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, _msg: LoadProjectUsers, _ctx: &mut Self::Context) -> WsResult {
|
||||||
use crate::db::users::LoadProjectUsers as Msg;
|
use crate::db::users::LoadProjectUsers as Msg;
|
||||||
|
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user_project()?.project_id;
|
||||||
let m = match block_on(self.db.send(Msg { project_id })) {
|
let m = match block_on(self.db.send(Msg { project_id })) {
|
||||||
Ok(Ok(v)) => Some(WsMsg::ProjectUsersLoaded(v)),
|
Ok(Ok(v)) => Some(WsMsg::ProjectUsersLoaded(v)),
|
||||||
_ => None,
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(m)
|
Ok(m)
|
||||||
}
|
}
|
||||||
@ -35,7 +42,10 @@ impl WsHandler<Register> for WebSocketActor {
|
|||||||
})) {
|
})) {
|
||||||
Ok(Ok(_)) => Some(WsMsg::SignUpSuccess),
|
Ok(Ok(_)) => Some(WsMsg::SignUpSuccess),
|
||||||
Ok(Err(_)) => Some(WsMsg::SignUpPairTaken),
|
Ok(Err(_)) => Some(WsMsg::SignUpPairTaken),
|
||||||
_ => None,
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.handle_msg(Authenticate { name, email }, ctx) {
|
match self.handle_msg(Authenticate { name, email }, ctx) {
|
||||||
@ -78,7 +88,14 @@ impl WsHandler<ProfileUpdate> for WebSocketActor {
|
|||||||
email,
|
email,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(_users)) => (),
|
Ok(Ok(_users)) => (),
|
||||||
_ => return Ok(None),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(WsMsg::ProfileUpdated))
|
Ok(Some(WsMsg::ProfileUpdated))
|
||||||
|
Loading…
Reference in New Issue
Block a user