Commit b110eb0e4d479472548896c41ca4c3445233348e
1 parent
eb9dfe04
Fixes #4 Basic upload endpoint done
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