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>) {
|
||||
if let Msg::ChangePage(Page::Project) = msg {
|
||||
model.page_content = PageContent::Invite(Box::new(InvitePage::default()));
|
||||
build_page_content(model);
|
||||
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> {
|
||||
let page = match &model.page_content {
|
||||
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::shared::aside::update(&msg, model, orders);
|
||||
match model.page {
|
||||
Page::Project | Page::AddIssue | Page::EditIssue(..) => project::update(msg, model, orders),
|
||||
Page::ProjectSettings => project_settings::update(msg, model, orders),
|
||||
|
@ -458,11 +458,14 @@ pub struct Model {
|
||||
|
||||
pub project: Option<Project>,
|
||||
pub user: Option<User>,
|
||||
pub current_user_project: Option<UserProject>,
|
||||
pub issues: Vec<Issue>,
|
||||
pub users: Vec<User>,
|
||||
pub comments: Vec<Comment>,
|
||||
pub issue_statuses: Vec<IssueStatus>,
|
||||
pub messages: Vec<Message>,
|
||||
pub user_projects: Vec<UserProject>,
|
||||
pub projects: Vec<Project>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
@ -475,8 +478,6 @@ impl Model {
|
||||
issue_form: None,
|
||||
project_form: None,
|
||||
comment_form: None,
|
||||
issues: vec![],
|
||||
users: vec![],
|
||||
comments_by_project_id: Default::default(),
|
||||
page: Page::Project,
|
||||
host_url,
|
||||
@ -484,9 +485,12 @@ impl Model {
|
||||
page_content: PageContent::Project(Box::new(ProjectPage::default())),
|
||||
modals: vec![],
|
||||
project: None,
|
||||
comments: vec![],
|
||||
current_user_project: None,
|
||||
about_tooltip_visible: false,
|
||||
messages_tooltip_visible: false,
|
||||
issues: vec![],
|
||||
users: vec![],
|
||||
comments: vec![],
|
||||
issue_statuses: vec![],
|
||||
messages: vec![Message {
|
||||
id: 0,
|
||||
@ -499,6 +503,22 @@ impl Model {
|
||||
created_at: chrono::NaiveDateTime::from_timestamp(4567890, 123),
|
||||
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 {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
||||
| Msg::ChangePage(Page::Profile) => {
|
||||
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref(), orders);
|
||||
let user = match model.user {
|
||||
Some(ref user) => user,
|
||||
_ => return,
|
||||
};
|
||||
model.page_content = PageContent::Profile(Box::new(ProfilePage::new(user)));
|
||||
init_load(model, orders);
|
||||
build_page_content(model);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@ -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> {
|
||||
let page = match &model.page_content {
|
||||
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::AddIssue)
|
||||
| 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::AddIssue)
|
||||
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||
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,
|
||||
);
|
||||
init_load(model, orders);
|
||||
}
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
|
||||
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> {
|
||||
let project_section = vec![
|
||||
breadcrumbs(model),
|
||||
|
@ -391,15 +391,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
||||
match msg {
|
||||
Msg::WebSocketChange(ref change) => match change {
|
||||
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
|
||||
enqueue_ws_msg(
|
||||
vec![
|
||||
WsMsg::ProjectRequest,
|
||||
WsMsg::IssueStatusesRequest,
|
||||
WsMsg::ProjectIssuesRequest,
|
||||
],
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
init_load(model, orders);
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => {
|
||||
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) => {
|
||||
build_page_content(model);
|
||||
if model.user.is_some() {
|
||||
enqueue_ws_msg(
|
||||
vec![
|
||||
WsMsg::ProjectRequest,
|
||||
WsMsg::IssueStatusesRequest,
|
||||
WsMsg::ProjectIssuesRequest,
|
||||
],
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
init_load(model, 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) {
|
||||
let page = match &mut model.page_content {
|
||||
PageContent::ProjectSettings(page) => page,
|
||||
|
@ -1,11 +1,25 @@
|
||||
use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::UserRole;
|
||||
use jirs_data::{UserRole, WsMsg};
|
||||
|
||||
use crate::model::{Model, Page};
|
||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||
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> {
|
||||
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),
|
||||
];
|
||||
|
||||
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(
|
||||
model,
|
||||
"Users",
|
||||
|
@ -5,7 +5,7 @@ use uuid::Uuid;
|
||||
|
||||
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_field::StyledField;
|
||||
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 {
|
||||
Msg::ChangePage(Page::SignIn) => {
|
||||
model.page_content = PageContent::SignIn(Box::new(SignInPage::default()));
|
||||
build_page_content(model);
|
||||
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> {
|
||||
let page = match &model.page_content {
|
||||
PageContent::SignIn(page) => page,
|
||||
|
@ -2,7 +2,7 @@ use seed::{prelude::*, *};
|
||||
|
||||
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_field::StyledField;
|
||||
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 {
|
||||
Msg::ChangePage(Page::SignUp) => {
|
||||
model.page_content = PageContent::SignUp(Box::new(SignUpPage::default()));
|
||||
build_page_content(model);
|
||||
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> {
|
||||
let page = match &model.page_content {
|
||||
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)))
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
// span![format!("{}", user.user_role)],
|
||||
li![
|
||||
class!["user"],
|
||||
span![user.name.as_str()],
|
||||
span![user.email.as_str()],
|
||||
span![format!("{}", user.user_role)],
|
||||
remove,
|
||||
]
|
||||
})
|
||||
|
@ -90,6 +90,33 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
WsMsg::ProjectLoaded(project) => {
|
||||
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
|
||||
WsMsg::ProjectIssuesLoaded(v) => {
|
||||
let mut v = v.clone();
|
||||
|
@ -21,6 +21,7 @@ pub trait ToVec {
|
||||
pub type IssueId = i32;
|
||||
pub type ProjectId = i32;
|
||||
pub type UserId = i32;
|
||||
pub type UserProjectId = i32;
|
||||
pub type CommentId = i32;
|
||||
pub type TokenId = i32;
|
||||
pub type IssueStatusId = i32;
|
||||
@ -468,10 +469,21 @@ pub struct User {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub avatar_url: Option<String>,
|
||||
pub project_id: ProjectId,
|
||||
pub created_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))]
|
||||
@ -533,6 +545,7 @@ impl From<Issue> for UpdateIssuePayload {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "backend", derive(Queryable))]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Message {
|
||||
pub id: MessageId,
|
||||
@ -656,6 +669,7 @@ pub enum IssueFieldId {
|
||||
pub enum WsMsg {
|
||||
Ping,
|
||||
Pong,
|
||||
Die,
|
||||
|
||||
// auth
|
||||
AuthorizeRequest(Uuid),
|
||||
@ -697,6 +711,9 @@ pub enum WsMsg {
|
||||
// project page
|
||||
ProjectRequest,
|
||||
ProjectLoaded(Project),
|
||||
ProjectsLoad,
|
||||
ProjectsLoaded(Vec<Project>),
|
||||
|
||||
ProjectIssuesRequest,
|
||||
ProjectIssuesLoaded(Vec<Issue>),
|
||||
ProjectUsersRequest,
|
||||
@ -734,6 +751,12 @@ pub enum WsMsg {
|
||||
ProfileUpdate(EmailString, UsernameString),
|
||||
ProfileUpdated,
|
||||
|
||||
// user projects
|
||||
UserProjectLoad,
|
||||
UserProjectLoaded(Vec<UserProject>),
|
||||
UserProjectSetCurrent(UserProjectId),
|
||||
UserProjectCurrentChanged(UserProject),
|
||||
|
||||
// messages
|
||||
Message(Message),
|
||||
MessagesRequest,
|
||||
|
@ -8,16 +8,6 @@ repository = "https://gitlab.com/adrian.wozniak/jirs"
|
||||
license = "MPL-2.0"
|
||||
#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]]
|
||||
name = "jirs_server"
|
||||
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)
|
||||
values ('backlog', 1, 1), ('selected', 1, 2), ('in_progress', 1, 3), ('done', 1, 4);
|
||||
|
||||
insert into users (project_id, email, name, avatar_url) values (
|
||||
1,
|
||||
insert into users (email, name, avatar_url) values (
|
||||
'john@example.com',
|
||||
'John Doe',
|
||||
'http://cdn.onlinewebfonts.com/svg/img_553934.png'
|
||||
), (
|
||||
1,
|
||||
'kate@exampe.com',
|
||||
'Kate Snow',
|
||||
'http://www.asthmamd.org/images/icon_user_6.png'
|
||||
), (
|
||||
1,
|
||||
'mike@example.com',
|
||||
'Mike Keningham',
|
||||
'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',
|
||||
'sent',
|
||||
|
@ -8,7 +8,7 @@ use jirs_data::{
|
||||
|
||||
use crate::db::DbExecutor;
|
||||
use crate::errors::ServiceErrors;
|
||||
use crate::models::{InvitationForm, UserForm};
|
||||
use crate::models::InvitationForm;
|
||||
|
||||
pub struct ListInvitation {
|
||||
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 {
|
||||
use crate::schema::invitations::dsl::*;
|
||||
use crate::schema::users::dsl::users;
|
||||
|
||||
let conn = &self
|
||||
.pool
|
||||
@ -181,17 +180,26 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
||||
.execute(conn)
|
||||
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||
|
||||
let form = UserForm {
|
||||
name: invitation.name,
|
||||
email: invitation.email,
|
||||
avatar_url: None,
|
||||
project_id: invitation.project_id,
|
||||
let user: User = {
|
||||
use crate::schema::users::dsl::*;
|
||||
|
||||
let query = diesel::insert_into(users)
|
||||
.values((name.eq(invitation.name), email.eq(invitation.email)));
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||
query
|
||||
.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)))?;
|
||||
};
|
||||
let query = diesel::insert_into(users).values(form);
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||
let user: User = query
|
||||
.get_result(conn)
|
||||
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ pub mod issue_statuses;
|
||||
pub mod issues;
|
||||
pub mod projects;
|
||||
pub mod tokens;
|
||||
pub mod user_projects;
|
||||
pub mod users;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -3,10 +3,11 @@ use diesel::pg::Pg;
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use jirs_data::{Project, ProjectCategory, TimeTracking};
|
||||
use jirs_data::{Project, ProjectCategory, TimeTracking, UserId};
|
||||
|
||||
use crate::db::DbExecutor;
|
||||
use crate::errors::ServiceErrors;
|
||||
use crate::schema::projects::all_columns;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LoadCurrentProject {
|
||||
@ -83,3 +84,34 @@ impl Handler<UpdateProject> for DbExecutor {
|
||||
.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::errors::ServiceErrors;
|
||||
use crate::models::{CreateProjectForm, UserForm};
|
||||
use crate::models::CreateProjectForm;
|
||||
use crate::schema::users::all_columns;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FindUser {
|
||||
@ -54,6 +55,7 @@ impl Handler<LoadProjectUsers> for DbExecutor {
|
||||
type Result = Result<Vec<User>, ServiceErrors>;
|
||||
|
||||
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::*;
|
||||
|
||||
let conn = &self
|
||||
@ -61,7 +63,11 @@ impl Handler<LoadProjectUsers> for DbExecutor {
|
||||
.get()
|
||||
.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));
|
||||
users_query
|
||||
.load(conn)
|
||||
@ -142,19 +148,31 @@ impl Handler<Register> for DbExecutor {
|
||||
.get_result(conn)
|
||||
.map_err(|_| ServiceErrors::RegisterCollision)?;
|
||||
|
||||
let form = UserForm {
|
||||
name: msg.name,
|
||||
email: msg.email,
|
||||
avatar_url: None,
|
||||
project_id: project.id,
|
||||
let user: User = {
|
||||
let insert_user_query =
|
||||
diesel::insert_into(users).values((name.eq(msg.name), email.eq(msg.email)));
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&insert_user_query));
|
||||
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));
|
||||
match insert_user_query.execute(conn) {
|
||||
Ok(_) => (),
|
||||
_ => return Err(ServiceErrors::RegisterCollision),
|
||||
};
|
||||
{
|
||||
use crate::schema::user_projects::dsl::*;
|
||||
let insert_user_project_query = diesel::insert_into(user_projects).values((
|
||||
user_id.eq(user.id),
|
||||
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(())
|
||||
}
|
||||
@ -287,11 +305,13 @@ mod tests {
|
||||
#[test]
|
||||
fn check_collision() {
|
||||
use crate::schema::projects::dsl::projects;
|
||||
use crate::schema::user_projects::dsl::user_projects;
|
||||
use crate::schema::users::dsl::users;
|
||||
|
||||
let pool = build_pool();
|
||||
let conn = &pool.get().unwrap();
|
||||
|
||||
diesel::delete(user_projects).execute(conn).unwrap();
|
||||
diesel::delete(users).execute(conn).unwrap();
|
||||
diesel::delete(projects).execute(conn).unwrap();
|
||||
|
||||
@ -306,16 +326,28 @@ mod tests {
|
||||
.get_result(conn)
|
||||
.unwrap();
|
||||
|
||||
let user_form = UserForm {
|
||||
name: "Foo".to_string(),
|
||||
email: "foo@example.com".to_string(),
|
||||
avatar_url: None,
|
||||
project_id: project.id,
|
||||
let user: User = {
|
||||
use crate::schema::users::dsl::*;
|
||||
diesel::insert_into(users)
|
||||
.values((
|
||||
name.eq("Foo".to_string()),
|
||||
email.eq("foo@example.com".to_string()),
|
||||
))
|
||||
.get_result(conn)
|
||||
.unwrap()
|
||||
};
|
||||
diesel::insert_into(users)
|
||||
.values(user_form)
|
||||
.execute(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)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(count_matching_users("Foo", "bar@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 email: String,
|
||||
pub avatar_url: Option<String>,
|
||||
pub project_id: i32,
|
||||
}
|
||||
|
||||
#[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! {
|
||||
use diesel::sql_types::*;
|
||||
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! {
|
||||
use diesel::sql_types::*;
|
||||
use jirs_data::sql::*;
|
||||
@ -445,12 +569,6 @@ table! {
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
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.
|
||||
///
|
||||
/// Its SQL type is `Timestamp`.
|
||||
@ -463,12 +581,6 @@ table! {
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
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 -> users (reporter_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!(
|
||||
comments,
|
||||
@ -491,7 +604,9 @@ allow_tables_to_appear_in_same_query!(
|
||||
issue_assignees,
|
||||
issues,
|
||||
issue_statuses,
|
||||
messages,
|
||||
projects,
|
||||
tokens,
|
||||
user_projects,
|
||||
users,
|
||||
);
|
||||
|
@ -9,6 +9,7 @@ use actix_multipart::{Field, Multipart};
|
||||
use actix_web::http::header::ContentDisposition;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{post, web, Error, HttpResponse};
|
||||
use futures::executor::block_on;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
#[cfg(feature = "aws-s3")]
|
||||
use rusoto_s3::{PutObjectRequest, S3Client, S3};
|
||||
@ -16,6 +17,7 @@ use rusoto_s3::{PutObjectRequest, S3Client, S3};
|
||||
use jirs_data::{User, UserId, WsMsg};
|
||||
|
||||
use crate::db::authorize_user::AuthorizeUser;
|
||||
use crate::db::user_projects::CurrentUserProject;
|
||||
use crate::db::users::UpdateAvatarUrl;
|
||||
use crate::db::DbExecutor;
|
||||
#[cfg(feature = "aws-s3")]
|
||||
@ -51,11 +53,21 @@ pub async fn upload(
|
||||
_ => 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) {
|
||||
(Some(user_id), Some(avatar_url)) => {
|
||||
(user_id, Some(avatar_url)) => {
|
||||
let user = update_user_avatar(user_id, avatar_url.clone(), db).await?;
|
||||
ws.send(BroadcastToChannel(
|
||||
user.project_id,
|
||||
project_id,
|
||||
WsMsg::AvatarUrlChanged(user.id, avatar_url),
|
||||
))
|
||||
.await
|
||||
|
@ -78,6 +78,9 @@ impl WsHandler<CheckAuthToken> for WebSocketActor {
|
||||
_ => return Ok(Some(WsMsg::AuthorizeExpired)),
|
||||
};
|
||||
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()));
|
||||
Ok(Some(WsMsg::AuthorizeLoaded(Ok(user))))
|
||||
}
|
||||
|
@ -16,7 +16,14 @@ impl WsHandler<LoadIssueComments> for WebSocketActor {
|
||||
issue_id: msg.issue_id,
|
||||
})) {
|
||||
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)))
|
||||
@ -38,7 +45,14 @@ impl WsHandler<CreateCommentPayload> for WebSocketActor {
|
||||
body: msg.body,
|
||||
})) {
|
||||
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)
|
||||
}
|
||||
@ -62,7 +76,14 @@ impl WsHandler<UpdateCommentPayload> for WebSocketActor {
|
||||
body,
|
||||
})) {
|
||||
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)? {
|
||||
self.broadcast(&v);
|
||||
@ -87,7 +108,14 @@ impl WsHandler<DeleteComment> for WebSocketActor {
|
||||
};
|
||||
match block_on(self.db.send(m)) {
|
||||
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)))
|
||||
|
@ -15,7 +15,14 @@ impl WsHandler<ListInvitation> for WebSocketActor {
|
||||
};
|
||||
let res = match block_on(self.db.send(invitations::ListInvitation { user_id })) {
|
||||
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)
|
||||
}
|
||||
@ -28,14 +35,15 @@ pub struct CreateInvitation {
|
||||
|
||||
impl WsHandler<CreateInvitation> for WebSocketActor {
|
||||
fn handle_msg(&mut self, msg: CreateInvitation, _ctx: &mut Self::Context) -> WsResult {
|
||||
let (user_id, inviter_name, project_id) = match self
|
||||
.current_user
|
||||
.as_ref()
|
||||
.map(|u| (u.id, u.name.clone(), u.project_id))
|
||||
{
|
||||
Some(id) => id,
|
||||
let project_id = match self.current_user_project.as_ref() {
|
||||
Some(up) => up.project_id,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
let (user_id, inviter_name) =
|
||||
match self.current_user.as_ref().map(|u| (u.id, u.name.clone())) {
|
||||
Some(id) => id,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
let CreateInvitation { email, name } = msg;
|
||||
let invitation = match block_on(self.db.send(invitations::CreateInvitation {
|
||||
@ -84,7 +92,14 @@ impl WsHandler<DeleteInvitation> for WebSocketActor {
|
||||
let DeleteInvitation { id } = msg;
|
||||
let res = match block_on(self.db.send(invitations::DeleteInvitation { id })) {
|
||||
Ok(Ok(_)) => None,
|
||||
_ => None,
|
||||
Ok(Err(e)) => {
|
||||
error!("{:?}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
@ -100,7 +115,14 @@ impl WsHandler<RevokeInvitation> for WebSocketActor {
|
||||
let RevokeInvitation { id } = msg;
|
||||
let res = match block_on(self.db.send(invitations::RevokeInvitation { 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)
|
||||
}
|
||||
@ -116,7 +138,14 @@ impl WsHandler<AcceptInvitation> for WebSocketActor {
|
||||
let AcceptInvitation { id } = msg;
|
||||
let res = match block_on(self.db.send(invitations::AcceptInvitation { 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)
|
||||
}
|
||||
|
@ -9,14 +9,21 @@ pub struct LoadIssueStatuses;
|
||||
|
||||
impl WsHandler<LoadIssueStatuses> for WebSocketActor {
|
||||
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(
|
||||
self.db
|
||||
.send(issue_statuses::LoadIssueStatuses { project_id }),
|
||||
) {
|
||||
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)
|
||||
}
|
||||
@ -29,7 +36,7 @@ pub struct CreateIssueStatus {
|
||||
|
||||
impl WsHandler<CreateIssueStatus> for WebSocketActor {
|
||||
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 msg = match block_on(self.db.send(issue_statuses::CreateIssueStatus {
|
||||
@ -38,7 +45,14 @@ impl WsHandler<CreateIssueStatus> for WebSocketActor {
|
||||
name,
|
||||
})) {
|
||||
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)
|
||||
}
|
||||
@ -50,7 +64,7 @@ pub struct DeleteIssueStatus {
|
||||
|
||||
impl WsHandler<DeleteIssueStatus> for WebSocketActor {
|
||||
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 msg = match block_on(self.db.send(issue_statuses::DeleteIssueStatus {
|
||||
@ -58,7 +72,14 @@ impl WsHandler<DeleteIssueStatus> for WebSocketActor {
|
||||
project_id,
|
||||
})) {
|
||||
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)
|
||||
}
|
||||
@ -72,7 +93,7 @@ pub struct UpdateIssueStatus {
|
||||
|
||||
impl WsHandler<UpdateIssueStatus> for WebSocketActor {
|
||||
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 {
|
||||
issue_status_id,
|
||||
@ -86,7 +107,14 @@ impl WsHandler<UpdateIssueStatus> for WebSocketActor {
|
||||
project_id,
|
||||
})) {
|
||||
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() {
|
||||
self.broadcast(ws_msg)
|
||||
|
@ -71,7 +71,14 @@ impl WsHandler<UpdateIssueHandler> for WebSocketActor {
|
||||
let assignees: Vec<IssueAssignee> =
|
||||
match block_on(self.db.send(LoadAssignees { issue_id: issue.id })) {
|
||||
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 {
|
||||
@ -102,7 +109,14 @@ impl WsHandler<CreateIssuePayload> for WebSocketActor {
|
||||
};
|
||||
let m = match block_on(self.db.send(msg)) {
|
||||
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)
|
||||
}
|
||||
@ -120,7 +134,14 @@ impl WsHandler<DeleteIssue> for WebSocketActor {
|
||||
.send(crate::db::issues::DeleteIssue { issue_id: 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)
|
||||
}
|
||||
@ -130,7 +151,7 @@ pub struct LoadIssues;
|
||||
|
||||
impl WsHandler<LoadIssues> for WebSocketActor {
|
||||
fn handle_msg(&mut self, _msg: LoadIssues, _ctx: &mut Self::Context) -> WsResult {
|
||||
let project_id = self.require_user()?.project_id;
|
||||
let project_id = self.require_user_project()?.project_id;
|
||||
|
||||
let issues: Vec<jirs_data::Issue> =
|
||||
match block_on(self.db.send(LoadProjectIssues { project_id })) {
|
||||
|
@ -6,9 +6,12 @@ use actix::{
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{get, web, Error, HttpRequest, HttpResponse};
|
||||
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::mail::MailExecutor;
|
||||
use crate::ws::auth::*;
|
||||
@ -25,6 +28,7 @@ pub mod invitations;
|
||||
pub mod issue_statuses;
|
||||
pub mod issues;
|
||||
pub mod projects;
|
||||
pub mod user_projects;
|
||||
pub mod users;
|
||||
|
||||
pub type WsResult = std::result::Result<Option<WsMsg>, WsMsg>;
|
||||
@ -36,8 +40,10 @@ trait WsMessageSender {
|
||||
struct WebSocketActor {
|
||||
db: Data<Addr<DbExecutor>>,
|
||||
mail: Data<Addr<MailExecutor>>,
|
||||
current_user: Option<jirs_data::User>,
|
||||
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 {
|
||||
@ -62,12 +68,12 @@ impl Handler<InnerMsg> for WebSocketActor {
|
||||
|
||||
impl WebSocketActor {
|
||||
fn broadcast(&self, msg: &WsMsg) {
|
||||
let user = match self.current_user.as_ref() {
|
||||
Some(u) => u,
|
||||
let project_id = match self.require_user_project() {
|
||||
Ok(up) => up.project_id,
|
||||
_ => return,
|
||||
};
|
||||
self.addr
|
||||
.do_send(InnerMsg::BroadcastToChannel(user.project_id, msg.clone()));
|
||||
.do_send(InnerMsg::BroadcastToChannel(project_id, msg.clone()));
|
||||
}
|
||||
|
||||
fn handle_ws_msg(
|
||||
@ -179,13 +185,18 @@ impl WebSocketActor {
|
||||
async fn join_channel(&self, addr: Recipient<InnerMsg>) {
|
||||
info!("joining channel...");
|
||||
info!(" current user {:?}", self.current_user);
|
||||
|
||||
let user = match self.current_user.as_ref() {
|
||||
None => return,
|
||||
Some(u) => u,
|
||||
};
|
||||
let project_id = match self.require_user_project() {
|
||||
Ok(user_project) => user_project.project_id,
|
||||
_ => return,
|
||||
};
|
||||
match self
|
||||
.addr
|
||||
.send(InnerMsg::Join(user.project_id, user.id, addr))
|
||||
.send(InnerMsg::Join(project_id, user.id, addr))
|
||||
.await
|
||||
{
|
||||
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
|
||||
.as_ref()
|
||||
.map(|u| u)
|
||||
.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 {
|
||||
@ -226,9 +267,12 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
||||
|
||||
fn finished(&mut self, ctx: &mut Self::Context) {
|
||||
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(
|
||||
user.project_id,
|
||||
up.project_id,
|
||||
user.id,
|
||||
ctx.address().recipient(),
|
||||
));
|
||||
@ -353,6 +397,8 @@ pub async fn index(
|
||||
db,
|
||||
mail,
|
||||
current_user: None,
|
||||
current_user_project: None,
|
||||
current_project: None,
|
||||
addr: ws_server.get_ref().clone(),
|
||||
},
|
||||
&req,
|
||||
|
@ -9,7 +9,7 @@ pub struct CurrentProject;
|
||||
|
||||
impl WsHandler<CurrentProject> for WebSocketActor {
|
||||
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 })) {
|
||||
Ok(Ok(project)) => Some(WsMsg::ProjectLoaded(project)),
|
||||
@ -28,7 +28,7 @@ impl WsHandler<CurrentProject> for WebSocketActor {
|
||||
|
||||
impl WsHandler<UpdateProjectPayload> for WebSocketActor {
|
||||
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 {
|
||||
project_id,
|
||||
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 {
|
||||
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 })) {
|
||||
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)
|
||||
}
|
||||
@ -35,7 +42,10 @@ impl WsHandler<Register> for WebSocketActor {
|
||||
})) {
|
||||
Ok(Ok(_)) => Some(WsMsg::SignUpSuccess),
|
||||
Ok(Err(_)) => Some(WsMsg::SignUpPairTaken),
|
||||
_ => None,
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
match self.handle_msg(Authenticate { name, email }, ctx) {
|
||||
@ -78,7 +88,14 @@ impl WsHandler<ProfileUpdate> for WebSocketActor {
|
||||
email,
|
||||
})) {
|
||||
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))
|
||||
|
Loading…
Reference in New Issue
Block a user