Showing
9 changed files
with
76 additions
and
18 deletions
@@ -25,17 +25,21 @@ edition = "2018" | @@ -25,17 +25,21 @@ edition = "2018" | ||
25 | 25 | ||
26 | [dependencies] | 26 | [dependencies] |
27 | actix-files = "0.2" | 27 | actix-files = "0.2" |
28 | -actix-web = "2.0" | ||
29 | actix-rt = "1.1.1" | 28 | actix-rt = "1.1.1" |
29 | +actix-web = "2.0" | ||
30 | +anyhow = "1.0" | ||
30 | artshop-common = { path = "../common" } | 31 | artshop-common = { path = "../common" } |
32 | +async-std = "^1.10" | ||
33 | +chrono = "0.4.15" | ||
31 | diesel = { version = "1.4.7", features = ["sqlite", "r2d2"]} | 34 | diesel = { version = "1.4.7", features = ["sqlite", "r2d2"]} |
32 | -r2d2 = "0.8.9" | 35 | +diffy = "0.2" |
33 | dotenv = "0.15.0" | 36 | dotenv = "0.15.0" |
37 | +flate2 = "^1.0" | ||
38 | +futures = "^0.3" | ||
39 | +listenfd = "0.3" | ||
40 | +r2d2 = "0.8.9" | ||
34 | serde = "1.0" | 41 | serde = "1.0" |
35 | serde_derive = "1.0" | 42 | serde_derive = "1.0" |
36 | serde_json = "1.0" | 43 | serde_json = "1.0" |
37 | -anyhow = "1.0" | ||
38 | -chrono = "0.4.15" | ||
39 | -listenfd = "0.3" | ||
40 | -diffy = "0.2" | ||
41 | -flate2 = "^1.0" | 44 | +#tokio = { version = "1", features = ["full"] } |
45 | +uuid = { version = "^0.8", features = ["v4"] } |
@@ -15,6 +15,7 @@ use diesel::r2d2::{self, ConnectionManager}; | @@ -15,6 +15,7 @@ use diesel::r2d2::{self, ConnectionManager}; | ||
15 | use diesel::SqliteConnection; | 15 | use diesel::SqliteConnection; |
16 | use listenfd::ListenFd; | 16 | use listenfd::ListenFd; |
17 | use routes::markdown::get_markdown; | 17 | use routes::markdown::get_markdown; |
18 | +use routes::upload::upload; | ||
18 | 19 | ||
19 | pub(crate) type Pool = r2d2::Pool<ConnectionManager<SqliteConnection>>; | 20 | pub(crate) type Pool = r2d2::Pool<ConnectionManager<SqliteConnection>>; |
20 | 21 | ||
@@ -33,6 +34,9 @@ async fn main() -> std::io::Result<()> { | @@ -33,6 +34,9 @@ async fn main() -> std::io::Result<()> { | ||
33 | App::new() . data(database_pool.clone()) | 34 | App::new() . data(database_pool.clone()) |
34 | . service(actix_files::Files::new("/static", "./static")) | 35 | . service(actix_files::Files::new("/static", "./static")) |
35 | . service( web::scope("/api/v0") | 36 | . service( web::scope("/api/v0") |
37 | + . service( web::resource("/upload") | ||
38 | + . route(web::post().to(upload)) | ||
39 | + ) | ||
36 | . service( web::resource("/markdowns") | 40 | . service( web::resource("/markdowns") |
37 | . route(web::get().to(get_markdowns)) | 41 | . route(web::get().to(get_markdowns)) |
38 | ) | 42 | ) |
server/src/routes/upload.rs
0 → 100644
1 | +use actix_web::{Error, HttpResponse, web}; | ||
2 | +use anyhow::Result; | ||
3 | +use futures::stream::StreamExt; | ||
4 | +use async_std::{fs::OpenOptions, io::WriteExt}; | ||
5 | +use uuid::Uuid; | ||
6 | + | ||
7 | +pub async fn upload(mut body: web::Payload) -> Result<HttpResponse, Error> | ||
8 | +{ | ||
9 | + let mut output = OpenOptions::new(); | ||
10 | + output . create(true) | ||
11 | + . write(true); | ||
12 | + let mut output = output | ||
13 | + . open(format!("/tmp/upload_{}", Uuid::new_v4())) | ||
14 | + . await | ||
15 | + . unwrap(); | ||
16 | + | ||
17 | + while let Some(item) = body.next().await { | ||
18 | + output.write_all(&item?).await.unwrap(); | ||
19 | + } | ||
20 | + | ||
21 | + Ok(HttpResponse::Ok().finish()) | ||
22 | +} |
@@ -18,6 +18,7 @@ impl UploadApi { | @@ -18,6 +18,7 @@ impl UploadApi { | ||
18 | pub(crate) async fn store(&self, upload :&Upload) -> Result<&UploadApi> { | 18 | pub(crate) async fn store(&self, upload :&Upload) -> Result<&UploadApi> { |
19 | let response = self.client.post_stream( "/api/v0/upload" | 19 | let response = self.client.post_stream( "/api/v0/upload" |
20 | , &upload.mime_type() | 20 | , &upload.mime_type() |
21 | + , upload.size() | ||
21 | , upload.data() ).await?; | 22 | , upload.data() ).await?; |
22 | match response.status() { | 23 | match response.status() { |
23 | 200 => Ok(self), | 24 | 200 => Ok(self), |
1 | use js_sys::JsString; | 1 | use js_sys::JsString; |
2 | use mogwai::prelude::*; | 2 | use mogwai::prelude::*; |
3 | use wasm_bindgen::prelude::*; | 3 | use wasm_bindgen::prelude::*; |
4 | -use web_sys::{Window, window, Response, Request, RequestInit, RequestMode, ReadableStream}; | 4 | +use web_sys::{Window, window, Response, Request, RequestInit, RequestMode, Headers}; |
5 | use super::error::*; | 5 | use super::error::*; |
6 | 6 | ||
7 | use std::result::Result as StdResult; | 7 | use std::result::Result as StdResult; |
@@ -63,17 +63,20 @@ impl Client { | @@ -63,17 +63,20 @@ impl Client { | ||
63 | } | 63 | } |
64 | 64 | ||
65 | pub async fn post_stream( &self | 65 | pub async fn post_stream( &self |
66 | - , url :&str | 66 | + , url :&str |
67 | , mime_type :&str | 67 | , mime_type :&str |
68 | - , data :ReadableStream) -> Result<Response> { | 68 | + , length :usize |
69 | + , data :&JsValue ) -> Result<Response> { | ||
70 | + let headers = Headers::new()?; | ||
71 | + headers.set("Content-Type", mime_type)?; | ||
72 | + headers.set("Content-Length", &format!("{}", length))?; | ||
73 | + | ||
69 | let mut init = RequestInit::new(); | 74 | let mut init = RequestInit::new(); |
70 | let request = REQUEST( url | 75 | let request = REQUEST( url |
71 | , init . method("POST") | 76 | , init . method("POST") |
72 | . mode(RequestMode::Cors) | 77 | . mode(RequestMode::Cors) |
73 | - . body(Some(&data.into())) )?; | ||
74 | - | ||
75 | - request . headers() | ||
76 | - . set("Content-Type", mime_type)?; | 78 | + . headers(&headers.into()) |
79 | + . body(Some(data)) )?; | ||
77 | 80 | ||
78 | let response = JsFuture::from( self.window | 81 | let response = JsFuture::from( self.window |
79 | . fetch_with_request(&request)) | 82 | . fetch_with_request(&request)) |
@@ -85,12 +85,29 @@ pub(super) async fn upload_logic( mut rx_logic :broadcast::Receiver<UploadLogic> | @@ -85,12 +85,29 @@ pub(super) async fn upload_logic( mut rx_logic :broadcast::Receiver<UploadLogic> | ||
85 | } | 85 | } |
86 | }, | 86 | }, |
87 | UploadLogic::Upload => { | 87 | UploadLogic::Upload => { |
88 | + let mut remove_ids = vec![]; | ||
89 | + | ||
88 | for upload in uploads.read().await.iter() { | 90 | for upload in uploads.read().await.iter() { |
89 | match api.store(upload).await { | 91 | match api.store(upload).await { |
90 | - Ok(_) => (), | 92 | + Ok(_) => remove_ids.push(upload.id), |
91 | Err(e) => log::error!("{:?}", e), | 93 | Err(e) => log::error!("{:?}", e), |
92 | } | 94 | } |
93 | } | 95 | } |
96 | + | ||
97 | + for id in remove_ids.iter() { | ||
98 | + let mut found = None; | ||
99 | + | ||
100 | + for (upload, index) in uploads.read().await.iter().zip(0..) { | ||
101 | + if upload.id == *id { | ||
102 | + found = Some(index); | ||
103 | + break; | ||
104 | + } | ||
105 | + } | ||
106 | + | ||
107 | + if let Some(index) = found { | ||
108 | + uploads.list_patch_remove(index).unwrap(); | ||
109 | + } | ||
110 | + } | ||
94 | } | 111 | } |
95 | } | 112 | } |
96 | } | 113 | } |
1 | use mogwai::{prelude::*, utils::window}; | 1 | use mogwai::{prelude::*, utils::window}; |
2 | -use web_sys::{File, ImageBitmap, ReadableStream}; | 2 | +use web_sys::{File, ImageBitmap}; |
3 | +use wasm_bindgen::prelude::*; | ||
3 | 4 | ||
4 | use super::{view::upload_preview_view, logic::{upload_preview_logic, UploadLogic}}; | 5 | use super::{view::upload_preview_view, logic::{upload_preview_logic, UploadLogic}}; |
5 | 6 | ||
@@ -30,8 +31,12 @@ impl Upload { | @@ -30,8 +31,12 @@ impl Upload { | ||
30 | self.file.type_() | 31 | self.file.type_() |
31 | } | 32 | } |
32 | 33 | ||
33 | - pub(crate) fn data(&self) -> ReadableStream { | ||
34 | - self.file.stream() | 34 | + pub(crate) fn data(&self) -> &JsValue { |
35 | + &self.file | ||
36 | + } | ||
37 | + | ||
38 | + pub(crate) fn size(&self) -> usize { | ||
39 | + self.file.size() as usize | ||
35 | } | 40 | } |
36 | 41 | ||
37 | pub(super) fn bitmap(&self) -> ImageBitmap { | 42 | pub(super) fn bitmap(&self) -> ImageBitmap { |
Please
register
or
login
to post a comment