Preallocations
This commit is contained in:
parent
146be0184b
commit
c8730485d0
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -2042,9 +2042,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.94"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "line-wrap"
|
name = "line-wrap"
|
||||||
|
@ -16,6 +16,7 @@ use jirs_data::{User, UserId, WsMsg};
|
|||||||
use websocket_actor::server::InnerMsg::BroadcastToChannel;
|
use websocket_actor::server::InnerMsg::BroadcastToChannel;
|
||||||
use websocket_actor::server::WsServer;
|
use websocket_actor::server::WsServer;
|
||||||
|
|
||||||
|
#[cfg(feature = "aws-s3")]
|
||||||
#[post("/")]
|
#[post("/")]
|
||||||
pub async fn upload(
|
pub async fn upload(
|
||||||
mut payload: Multipart,
|
mut payload: Multipart,
|
||||||
@ -80,11 +81,74 @@ pub async fn upload(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "aws-s3"))]
|
||||||
|
#[post("/")]
|
||||||
|
pub async fn upload(
|
||||||
|
mut payload: Multipart,
|
||||||
|
db: Data<Addr<DbExecutor>>,
|
||||||
|
ws: Data<Addr<WsServer>>,
|
||||||
|
fs: Data<Addr<filesystem_actor::FileSystemExecutor>>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let mut user_id: Option<UserId> = None;
|
||||||
|
let mut avatar_url: Option<String> = None;
|
||||||
|
|
||||||
|
while let Ok(Some(field)) = payload.try_next().await {
|
||||||
|
let disposition: ContentDisposition = match field.content_disposition() {
|
||||||
|
Some(d) => d,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
if !disposition.is_form_data() {
|
||||||
|
return Ok(HttpResponse::BadRequest().finish());
|
||||||
|
}
|
||||||
|
match disposition.get_name() {
|
||||||
|
Some("token") => {
|
||||||
|
user_id = Some(handle_token(field, db.clone()).await?);
|
||||||
|
}
|
||||||
|
Some("avatar") => {
|
||||||
|
let id = user_id.ok_or_else(|| HttpResponse::Unauthorized().finish())?;
|
||||||
|
avatar_url = Some(
|
||||||
|
crate::handlers::upload_avatar_image::handle_image(
|
||||||
|
id,
|
||||||
|
field,
|
||||||
|
disposition,
|
||||||
|
fs.clone(),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => 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) {
|
||||||
|
(user_id, Some(avatar_url)) => {
|
||||||
|
let user = update_user_avatar(user_id, avatar_url.clone(), db).await?;
|
||||||
|
ws.send(BroadcastToChannel(
|
||||||
|
project_id,
|
||||||
|
WsMsg::AvatarUrlChanged(user.id, avatar_url),
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.map_err(|_| HttpResponse::UnprocessableEntity().finish())?;
|
||||||
|
Ok(HttpResponse::NoContent().finish())
|
||||||
|
}
|
||||||
|
_ => Ok(HttpResponse::UnprocessableEntity().finish()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_user_avatar(
|
async fn update_user_avatar(
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
new_url: String,
|
new_url: String,
|
||||||
db: Data<Addr<DbExecutor>>,
|
db: Data<Addr<DbExecutor>>,
|
||||||
) -> Result<User, Error> {
|
) -> Result<User, actix_web::Error> {
|
||||||
match db
|
match db
|
||||||
.send(UpdateAvatarUrl {
|
.send(UpdateAvatarUrl {
|
||||||
user_id,
|
user_id,
|
||||||
@ -96,16 +160,23 @@ async fn update_user_avatar(
|
|||||||
|
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!("{:?}", e);
|
error!("{:?}", e);
|
||||||
Err(HttpResponse::Unauthorized().finish().into())
|
Err(actix_web::Error::from(
|
||||||
|
HttpResponse::Unauthorized().finish(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{:?}", e);
|
error!("{:?}", e);
|
||||||
Err(HttpResponse::Unauthorized().finish().into())
|
Err(actix_web::Error::from(
|
||||||
|
HttpResponse::Unauthorized().finish(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_token(mut field: Field, db: Data<Addr<DbExecutor>>) -> Result<UserId, Error> {
|
async fn handle_token(
|
||||||
|
mut field: Field,
|
||||||
|
db: Data<Addr<DbExecutor>>,
|
||||||
|
) -> Result<UserId, actix_web::Error> {
|
||||||
let mut f: Vec<u8> = vec![];
|
let mut f: Vec<u8> = vec![];
|
||||||
while let Some(chunk) = field.next().await {
|
while let Some(chunk) = field.next().await {
|
||||||
let data = chunk.unwrap();
|
let data = chunk.unwrap();
|
||||||
|
@ -29,9 +29,9 @@ flate2 = { version = "*" }
|
|||||||
syntect = { version = "*" }
|
syntect = { version = "*" }
|
||||||
lazy_static = { version = "*" }
|
lazy_static = { version = "*" }
|
||||||
|
|
||||||
log = "0.4"
|
log = { version = "0.4" }
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = { version = "0.4" }
|
||||||
env_logger = "0.7"
|
env_logger = { version = "0.7" }
|
||||||
|
|
||||||
uuid = { version = "0.8.1", features = ["serde", "v4", "v5"] }
|
uuid = { version = "0.8.1", features = ["serde", "v4", "v5"] }
|
||||||
|
|
||||||
|
@ -7,10 +7,8 @@ use jirs_data::msg::WsError;
|
|||||||
use jirs_data::{Token, WsMsg};
|
use jirs_data::{Token, WsMsg};
|
||||||
use mail_actor::welcome::Welcome;
|
use mail_actor::welcome::Welcome;
|
||||||
|
|
||||||
use crate::{
|
use crate::server::InnerMsg;
|
||||||
db_or_debug_and_return, db_or_debug_or_fallback, mail_or_debug_and_return, WebSocketActor,
|
use crate::{db_or_debug_and_return, db_or_debug_or_fallback, mail_or_debug_and_return, *};
|
||||||
WsHandler, WsResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Authenticate {
|
pub struct Authenticate {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -69,7 +67,7 @@ impl WsHandler<CheckAuthToken> for WebSocketActor {
|
|||||||
self.current_user_project = self.load_user_project().ok();
|
self.current_user_project = self.load_user_project().ok();
|
||||||
self.current_project = self.load_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::<InnerMsg>()));
|
||||||
Ok(Some(WsMsg::AuthorizeLoaded(Ok((user, setting)))))
|
Ok(Some(WsMsg::AuthorizeLoaded(Ok((user, setting)))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use jirs_data::{
|
|||||||
DescriptionString, EndsAt, EpicId, IssueType, NameString, StartsAt, UserProject, WsMsg,
|
DescriptionString, EndsAt, EpicId, IssueType, NameString, StartsAt, UserProject, WsMsg,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{db_or_debug_and_return, WebSocketActor, WsHandler, WsResult};
|
use crate::{db_or_debug_and_return, *};
|
||||||
|
|
||||||
pub struct LoadEpics;
|
pub struct LoadEpics;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ trait WsMessageSender {
|
|||||||
fn send_msg(&mut self, msg: &jirs_data::WsMsg);
|
fn send_msg(&mut self, msg: &jirs_data::WsMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WebSocketActor {
|
pub struct WebSocketActor {
|
||||||
db: Data<Addr<DbExecutor>>,
|
db: Data<Addr<DbExecutor>>,
|
||||||
mail: Data<Addr<MailExecutor>>,
|
mail: Data<Addr<MailExecutor>>,
|
||||||
addr: Addr<WsServer>,
|
addr: Addr<WsServer>,
|
||||||
@ -36,13 +36,20 @@ struct WebSocketActor {
|
|||||||
current_project: Option<jirs_data::Project>,
|
current_project: Option<jirs_data::Project>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for WebSocketActor {
|
pub type WsCtx = ws::WebsocketContext<WebSocketActor>;
|
||||||
type Context = ws::WebsocketContext<WebSocketActor>;
|
|
||||||
|
impl actix::Actor for WebSocketActor {
|
||||||
|
type Context = WsCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WsMessageSender for ws::WebsocketContext<WebSocketActor> {
|
impl WsMessageSender for ws::WebsocketContext<WebSocketActor> {
|
||||||
fn send_msg(&mut self, msg: &WsMsg) {
|
fn send_msg(&mut self, msg: &WsMsg) {
|
||||||
self.binary(bincode::serialize(msg).unwrap())
|
match bincode::serialize(msg) {
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("{}", err);
|
||||||
|
}
|
||||||
|
Ok(v) => self.binary(v),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +335,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
|||||||
self.addr.do_send(InnerMsg::Leave(
|
self.addr.do_send(InnerMsg::Leave(
|
||||||
up.project_id,
|
up.project_id,
|
||||||
user.id,
|
user.id,
|
||||||
ctx.address().recipient(),
|
ctx.address().recipient::<InnerMsg>(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ctx.stop()
|
ctx.stop()
|
||||||
@ -337,7 +344,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
|||||||
|
|
||||||
pub trait WsHandler<Message>
|
pub trait WsHandler<Message>
|
||||||
where
|
where
|
||||||
Self: Actor,
|
Self: actix::Actor<Context = WsCtx>,
|
||||||
{
|
{
|
||||||
fn handle_msg(&mut self, msg: Message, _ctx: &mut <Self as Actor>::Context) -> WsResult;
|
fn handle_msg(&mut self, msg: Message, _ctx: &mut <Self as Actor>::Context) -> WsResult;
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ impl Model {
|
|||||||
issue_form: None,
|
issue_form: None,
|
||||||
project_form: None,
|
project_form: None,
|
||||||
comment_form: None,
|
comment_form: None,
|
||||||
comments_by_project_id: Default::default(),
|
comments_by_project_id: HashMap::with_capacity(1_000),
|
||||||
page_content: page.build_content(),
|
page_content: page.build_content(),
|
||||||
page,
|
page,
|
||||||
host_url,
|
host_url,
|
||||||
@ -291,22 +291,22 @@ impl Model {
|
|||||||
messages_tooltip_visible: false,
|
messages_tooltip_visible: false,
|
||||||
issues: vec![],
|
issues: vec![],
|
||||||
users: vec![],
|
users: vec![],
|
||||||
users_by_id: Default::default(),
|
users_by_id: HashMap::with_capacity(1_000),
|
||||||
user_settings: None,
|
user_settings: None,
|
||||||
comments: vec![],
|
comments: vec![],
|
||||||
comments_by_id: Default::default(),
|
comments_by_id: HashMap::with_capacity(1_000),
|
||||||
issue_statuses: vec![],
|
issue_statuses: vec![],
|
||||||
issue_statuses_by_id: Default::default(),
|
issue_statuses_by_id: HashMap::with_capacity(1_000),
|
||||||
issue_statuses_by_name: Default::default(),
|
issue_statuses_by_name: HashMap::with_capacity(1_000),
|
||||||
messages: vec![],
|
messages: vec![],
|
||||||
user_projects: vec![],
|
user_projects: vec![],
|
||||||
projects: vec![],
|
projects: vec![],
|
||||||
epics: vec![],
|
epics: vec![],
|
||||||
issues_by_id: Default::default(),
|
issues_by_id: HashMap::with_capacity(1_000),
|
||||||
show_extras: false,
|
show_extras: false,
|
||||||
epics_by_id: Default::default(),
|
epics_by_id: HashMap::with_capacity(1_000),
|
||||||
modals_stack: vec![],
|
modals_stack: vec![],
|
||||||
modals: Default::default(),
|
modals: Modals::default(),
|
||||||
key_triggers: std::rc::Rc::new(std::cell::RefCell::new(HashMap::with_capacity(20))),
|
key_triggers: std::rc::Rc::new(std::cell::RefCell::new(HashMap::with_capacity(20))),
|
||||||
distinct_key_up: crate::shared::on_event::distinct(),
|
distinct_key_up: crate::shared::on_event::distinct(),
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,15 @@ impl EpicsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_issues_per_epic(model: &Model) -> HashMap<EpicId, Vec<IssueId>> {
|
pub fn build_issues_per_epic(model: &Model) -> HashMap<EpicId, Vec<IssueId>> {
|
||||||
model.issues().iter().fold(HashMap::new(), |mut h, issue| {
|
model.issues().iter().fold(
|
||||||
if let Some(epic_id) = issue.epic_id.as_ref() {
|
HashMap::with_capacity(model.issues().len()),
|
||||||
h.entry(*epic_id).or_default().push(issue.id);
|
|mut h, issue| {
|
||||||
}
|
if let Some(epic_id) = issue.epic_id.as_ref() {
|
||||||
h
|
h.entry(*epic_id).or_default().push(issue.id);
|
||||||
})
|
}
|
||||||
|
h
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn issues(&self, epic_id: EpicId) -> Option<&Vec<IssueId>> {
|
pub fn issues(&self, epic_id: EpicId) -> Option<&Vec<IssueId>> {
|
||||||
|
@ -130,7 +130,7 @@ async fn update_avatar(data: FormData, host_url: String) -> Option<Msg> {
|
|||||||
let path = format!("{}/avatar/", host_url);
|
let path = format!("{}/avatar/", host_url);
|
||||||
let result = Request::new(path)
|
let result = Request::new(path)
|
||||||
.method(Method::Post)
|
.method(Method::Post)
|
||||||
.body(data.into())
|
.body(&data)
|
||||||
.fetch()
|
.fetch()
|
||||||
.await;
|
.await;
|
||||||
let response = match result {
|
let response = match result {
|
||||||
|
@ -68,10 +68,15 @@ impl ProjectPage {
|
|||||||
issues.collect()
|
issues.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let issues_per_epic_id = issues.into_iter().fold(HashMap::new(), |mut m, issue| {
|
let issues_per_epic_id = {
|
||||||
m.entry(issue.epic_id).or_insert_with(Vec::new).push(issue);
|
let issues_len = issues.len();
|
||||||
m
|
issues
|
||||||
});
|
.into_iter()
|
||||||
|
.fold(HashMap::with_capacity(issues_len), |mut m, issue| {
|
||||||
|
m.entry(issue.epic_id).or_insert_with(Vec::new).push(issue);
|
||||||
|
m
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
epics
|
epics
|
||||||
.map(|epic| {
|
.map(|epic| {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use jirs_data::{IssueStatus, IssueStatusId, ProjectFieldId, UpdateProjectPayload, WsMsg};
|
use jirs_data::{IssueStatus, IssueStatusId, ProjectFieldId, UpdateProjectPayload, WsMsg};
|
||||||
use seed::error;
|
|
||||||
use seed::prelude::Orders;
|
use seed::prelude::Orders;
|
||||||
|
|
||||||
use crate::components::styled_select::StyledSelectChanged;
|
use crate::components::styled_select::StyledSelectChanged;
|
||||||
|
@ -15,10 +15,7 @@ path = "./src/main.rs"
|
|||||||
[features]
|
[features]
|
||||||
aws-s3 = ["amazon-actor"]
|
aws-s3 = ["amazon-actor"]
|
||||||
local-storage = ["filesystem-actor"]
|
local-storage = ["filesystem-actor"]
|
||||||
default = [
|
default = ["local-storage"]
|
||||||
"aws-s3",
|
|
||||||
"local-storage",
|
|
||||||
]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.10.0" }
|
actix = { version = "0.10.0" }
|
||||||
|
@ -20,9 +20,10 @@ hi = []
|
|||||||
mail = []
|
mail = []
|
||||||
web = ["aws-s3", "local-storage"]
|
web = ["aws-s3", "local-storage"]
|
||||||
websocket = []
|
websocket = []
|
||||||
|
default = ["local-storage", "database", "hi", "mail", "web", "websocket"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "*"
|
serde = { version = "*" }
|
||||||
toml = { version = "*" }
|
toml = { version = "*" }
|
||||||
|
|
||||||
# Amazon S3
|
# Amazon S3
|
||||||
|
@ -17,8 +17,8 @@ backend = ["diesel", "actix", "derive_enum_sql"]
|
|||||||
frontend = []
|
frontend = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "*"
|
serde = { version = "*" }
|
||||||
serde_json = "*"
|
serde_json = { version = "*" }
|
||||||
chrono = { version = "*", features = ["serde"] }
|
chrono = { version = "*", features = ["serde"] }
|
||||||
uuid = { version = ">=0.7.0, <0.9.0", features = ["serde"] }
|
uuid = { version = ">=0.7.0, <0.9.0", features = ["serde"] }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user