Commit 76eebfe80dd18350eacccc38c45e10fe2b441952
1 parent
abe77b68
Added first image processing decoupled from the upload
Showing
6 changed files
with
125 additions
and
30 deletions
... | ... | @@ -20,9 +20,10 @@ diffy = "0.2" |
20 | 20 | dotenv = "0.15.0" |
21 | 21 | flate2 = "^1.0" |
22 | 22 | futures = "^0.3" |
23 | +futures-util = { version = "0", features = ["std"] } | |
23 | 24 | listenfd = "0.3" |
24 | 25 | r2d2 = "0.8.9" |
25 | 26 | serde = { version = "^1.0", features = ["derive"] } |
26 | 27 | serde_derive = "1.0" |
27 | 28 | serde_json = "1.0" |
28 | -uuid = { version = "^0.8", features = ["v4"] } | |
29 | +uuid = { version = "^0.8", features = ["v4", "v5"] } | ... | ... |
... | ... | @@ -5,33 +5,94 @@ mod error; |
5 | 5 | mod models; |
6 | 6 | mod routes; |
7 | 7 | mod schema; |
8 | +mod uuid; | |
8 | 9 | |
9 | 10 | use crate::routes::markdown::*; |
10 | 11 | use crate::routes::other::*; |
11 | 12 | use crate::routes::user::*; |
13 | +use crate::uuid::Uuid; | |
12 | 14 | |
13 | 15 | use actix_web::{guard, web, App, HttpResponse, HttpServer}; |
16 | +use async_std::channel::Receiver; | |
17 | +use async_std::channel::Sender; | |
18 | +use async_std::channel::bounded; | |
19 | +use async_std::fs::File; | |
14 | 20 | use diesel::r2d2::{self, ConnectionManager}; |
15 | 21 | use diesel::SqliteConnection; |
22 | +use futures::AsyncReadExt; | |
23 | +use futures::AsyncSeekExt; | |
16 | 24 | use listenfd::ListenFd; |
17 | 25 | use routes::markdown::get_markdown; |
18 | 26 | use routes::upload::upload; |
27 | +use std::io::SeekFrom; | |
28 | +use std::sync::Arc; | |
19 | 29 | |
20 | 30 | pub(crate) type Pool = r2d2::Pool<ConnectionManager<SqliteConnection>>; |
21 | 31 | |
32 | +#[derive(Clone)] | |
33 | +pub struct AppData { | |
34 | + pub database_pool: Arc<Pool>, | |
35 | + pub tx_upload_worker: Sender<String>, | |
36 | +} | |
37 | + | |
38 | +async fn read_at( f :&mut File | |
39 | + , pos :SeekFrom | |
40 | + , buf :&mut [u8]) -> std::io::Result<()> { | |
41 | + f.seek(pos).await?; | |
42 | + f.read_exact(buf).await | |
43 | +} | |
44 | + | |
45 | +async fn get_sample( f :&mut File | |
46 | + , buf :&mut [u8]) -> std::io::Result<()> { | |
47 | + let file_len = f.metadata().await?.len(); | |
48 | + let chunk_size = buf.len() / 3; | |
49 | + | |
50 | + read_at(f, SeekFrom::Start(0), &mut buf[0..chunk_size]).await?; | |
51 | + if file_len >= 2 * chunk_size as u64 { | |
52 | + read_at( f | |
53 | + , SeekFrom::End(-(chunk_size as i64)) | |
54 | + , &mut buf[2*chunk_size..]).await?; | |
55 | + } | |
56 | + if file_len >= 3 * chunk_size as u64 { | |
57 | + read_at( f | |
58 | + , SeekFrom::Start((file_len-chunk_size as u64) / 2) | |
59 | + , &mut buf[chunk_size..2*chunk_size]).await?; | |
60 | + } | |
61 | + | |
62 | + Ok(()) | |
63 | +} | |
64 | + | |
22 | 65 | #[actix_rt::main] |
23 | 66 | async fn main() -> std::io::Result<()> { |
24 | 67 | let mut listenfd = ListenFd::from_env(); |
25 | 68 | |
26 | 69 | dotenv::dotenv().ok(); |
27 | 70 | |
71 | + let ( tx_upload_worker | |
72 | + , rx_upload_worker ) :( Sender<String> | |
73 | + , Receiver<String> ) = bounded(32); | |
74 | + | |
75 | + let _upload_worker = actix_rt::spawn(async move { | |
76 | + while let Ok(filename) = rx_upload_worker.recv().await { | |
77 | + let mut f = File::open(&filename).await.unwrap(); | |
78 | + let mut buf = vec!['.' as u8; 3 * 4096]; | |
79 | + get_sample(&mut f, buf.as_mut()).await.unwrap(); | |
80 | + println!("[UPLOAD WORKER] filename: {}", filename); | |
81 | + println!( "[UPLOAD WORKER] uuid: {}" | |
82 | + , Uuid::get("some.unique.namespace", buf.as_mut()) ); | |
83 | + } | |
84 | + }); | |
85 | + | |
28 | 86 | let database_url = std::env::var("DATABASE_URL").expect("NOT FOUND"); |
29 | 87 | let database_pool = Pool::builder() |
30 | 88 | .build(ConnectionManager::new(database_url)) |
31 | 89 | .unwrap(); |
32 | 90 | |
91 | + let database_pool = Arc::new(database_pool); | |
92 | + let app_data = AppData { database_pool, tx_upload_worker }; | |
93 | + | |
33 | 94 | let server = HttpServer::new(move || { |
34 | - App::new() . data(database_pool.clone()) | |
95 | + App::new() . data(app_data.clone()) | |
35 | 96 | . service(actix_files::Files::new("/static", "./static")) |
36 | 97 | . service( web::scope("/api/v0") |
37 | 98 | . service( web::resource("/upload") | ... | ... |
1 | -use crate::models::markdown; | |
2 | -use crate::Pool; | |
1 | +use crate::{models::markdown, AppData}; | |
3 | 2 | |
4 | 3 | use actix_web::{Error, HttpResponse, web}; |
5 | 4 | use anyhow::Result; |
... | ... | @@ -11,36 +10,40 @@ pub struct Patchset { |
11 | 10 | patch: Option<i32>, |
12 | 11 | } |
13 | 12 | |
14 | -pub async fn get_markdowns(pool: web::Data<Pool>) | |
13 | +pub async fn get_markdowns(app_data: web::Data<AppData>) | |
15 | 14 | -> Result<HttpResponse, Error> |
16 | 15 | { |
17 | - Ok( web::block(move || markdown::get_markdowns(pool.into_inner())) | |
16 | + let pool = app_data.database_pool.clone(); | |
17 | + | |
18 | + Ok( web::block(move || markdown::get_markdowns(pool)) | |
18 | 19 | . await |
19 | 20 | . map(|markdowns| HttpResponse::Ok().json(markdowns)) |
20 | 21 | . map_err(|_| HttpResponse::InternalServerError())? |
21 | 22 | ) |
22 | 23 | } |
23 | 24 | |
24 | -pub async fn get_markdown( pool: web::Data<Pool> | |
25 | +pub async fn get_markdown( app_data: web::Data<AppData> | |
25 | 26 | , name: web::Path<String> |
26 | 27 | , patch: web::Query<Patchset> |
27 | 28 | ) -> Result<HttpResponse, Error> |
28 | 29 | { |
29 | - let pool = pool.into_inner(); | |
30 | + let pool = app_data.database_pool.clone(); | |
30 | 31 | let name = name.into_inner(); |
31 | 32 | let patch = patch.into_inner(); |
32 | 33 | |
33 | - Ok( web::block(move || markdown::get_markdown(pool, name.as_str(), patch.patch)) | |
34 | + Ok( web::block(move || markdown::get_markdown( pool | |
35 | + , name.as_str() | |
36 | + , patch.patch) ) | |
34 | 37 | . await |
35 | 38 | . map(|markdowns| HttpResponse::Ok().json(markdowns)) |
36 | 39 | . map_err(|_| HttpResponse::InternalServerError())? |
37 | 40 | ) |
38 | 41 | } |
39 | 42 | |
40 | -pub async fn get_patches( pool: web::Data<Pool> | |
43 | +pub async fn get_patches( app_data: web::Data<AppData> | |
41 | 44 | , name: web::Path<String> |
42 | 45 | ) -> Result<HttpResponse, Error> { |
43 | - let pool = pool.into_inner(); | |
46 | + let pool = app_data.database_pool.clone(); | |
44 | 47 | let name = name.into_inner(); |
45 | 48 | |
46 | 49 | Ok( web::block(move || markdown::get_patches(pool, name.as_str())) |
... | ... | @@ -50,12 +53,12 @@ pub async fn get_patches( pool: web::Data<Pool> |
50 | 53 | ) |
51 | 54 | } |
52 | 55 | |
53 | -pub async fn update_markdown( pool: web::Data<Pool> | |
56 | +pub async fn update_markdown( app_data: web::Data<AppData> | |
54 | 57 | , name: web::Path<String> |
55 | 58 | , item: web::Json<MarkdownJson> ) |
56 | 59 | -> Result<HttpResponse, Error> |
57 | 60 | { |
58 | - let pool = pool.into_inner(); | |
61 | + let pool = app_data.database_pool.clone(); | |
59 | 62 | let name = name.into_inner(); |
60 | 63 | let item = item.into_inner(); |
61 | 64 | ... | ... |
... | ... | @@ -4,19 +4,25 @@ use futures::stream::StreamExt; |
4 | 4 | use async_std::{fs::OpenOptions, io::WriteExt}; |
5 | 5 | use uuid::Uuid; |
6 | 6 | |
7 | -pub async fn upload(mut body: web::Payload) -> Result<HttpResponse, Error> | |
7 | +use crate::AppData; | |
8 | + | |
9 | +pub async fn upload( app_data: web::Data<AppData> | |
10 | + , mut body: web::Payload) -> Result<HttpResponse, Error> | |
8 | 11 | { |
12 | + let worker = app_data.tx_upload_worker.clone(); | |
13 | + | |
14 | + let upload_filename = format!("/tmp/upload_{}", Uuid::new_v4()); | |
9 | 15 | let mut output = OpenOptions::new(); |
10 | 16 | output . create(true) |
11 | 17 | . write(true); |
12 | - let mut output = output | |
13 | - . open(format!("/tmp/upload_{}", Uuid::new_v4())) | |
14 | - . await | |
15 | - . unwrap(); | |
18 | + let mut output = output.open(&upload_filename).await?; | |
16 | 19 | |
17 | 20 | while let Some(item) = body.next().await { |
18 | - output.write_all(&item?).await.unwrap(); | |
21 | + output.write_all(&item?).await?; | |
19 | 22 | } |
20 | 23 | |
24 | + // TODO handle this as error response... | |
25 | + worker.send(upload_filename).await.unwrap(); | |
26 | + | |
21 | 27 | Ok(HttpResponse::Ok().finish()) |
22 | 28 | } | ... | ... |
1 | 1 | use crate::models::user::{self, Action}; |
2 | -use crate::Pool; | |
2 | +use crate::AppData; | |
3 | 3 | |
4 | 4 | use actix_web::{Error, HttpResponse, web}; |
5 | 5 | use anyhow::Result; |
6 | 6 | |
7 | -pub async fn create_user( pool: web::Data<Pool> | |
7 | +pub async fn create_user( app_data: web::Data<AppData> | |
8 | 8 | , item: web::Json<user::UserJson> ) |
9 | 9 | -> Result<HttpResponse, Error> |
10 | 10 | { |
11 | - let pool = pool.into_inner(); | |
11 | + let pool = app_data.database_pool.clone(); | |
12 | 12 | let item = item.into_inner(); |
13 | 13 | |
14 | 14 | Ok(web::block(move || user::create_user(pool, item)) |
... | ... | @@ -21,19 +21,21 @@ pub async fn create_user( pool: web::Data<Pool> |
21 | 21 | . map_err(|_| HttpResponse::InternalServerError())?) |
22 | 22 | } |
23 | 23 | |
24 | -pub async fn get_users(pool: web::Data<Pool>) | |
24 | +pub async fn get_users(app_data: web::Data<AppData>) | |
25 | 25 | -> Result<HttpResponse, Error> |
26 | 26 | { |
27 | - Ok(web::block(move || user::get_users(pool.into_inner())) | |
27 | + let pool = app_data.database_pool.clone(); | |
28 | + | |
29 | + Ok(web::block(move || user::get_users(pool)) | |
28 | 30 | . await |
29 | 31 | . map(|users| HttpResponse::Ok().json(users)) |
30 | 32 | . map_err(|_| HttpResponse::InternalServerError())?) |
31 | 33 | } |
32 | 34 | |
33 | -pub async fn get_user(pool: web::Data<Pool>, id: web::Path<i32>) | |
35 | +pub async fn get_user(app_data: web::Data<AppData>, id: web::Path<i32>) | |
34 | 36 | -> Result<HttpResponse, Error> |
35 | 37 | { |
36 | - let pool = pool.into_inner(); | |
38 | + let pool = app_data.database_pool.clone(); | |
37 | 39 | let id = id.into_inner(); |
38 | 40 | |
39 | 41 | Ok(web::block(move || user::get_user(pool, id)) |
... | ... | @@ -42,10 +44,10 @@ pub async fn get_user(pool: web::Data<Pool>, id: web::Path<i32>) |
42 | 44 | . map_err(|_| HttpResponse::InternalServerError())?) |
43 | 45 | } |
44 | 46 | |
45 | -pub async fn delete_user(pool: web::Data<Pool>, id: web::Path<i32>) | |
47 | +pub async fn delete_user(app_data: web::Data<AppData>, id: web::Path<i32>) | |
46 | 48 | -> Result<HttpResponse, Error> |
47 | 49 | { |
48 | - let pool = pool.into_inner(); | |
50 | + let pool = app_data.database_pool.clone(); | |
49 | 51 | let id = id.into_inner(); |
50 | 52 | |
51 | 53 | Ok(web::block(move || user::delete_user(pool, id)) |
... | ... | @@ -54,12 +56,12 @@ pub async fn delete_user(pool: web::Data<Pool>, id: web::Path<i32>) |
54 | 56 | . map_err(|_| HttpResponse::InternalServerError())?) |
55 | 57 | } |
56 | 58 | |
57 | -pub async fn update_user( pool: web::Data<Pool> | |
59 | +pub async fn update_user( app_data: web::Data<AppData> | |
58 | 60 | , id: web::Path<i32> |
59 | 61 | , item: web::Json<user::UserJson> ) |
60 | 62 | -> Result<HttpResponse, Error> |
61 | 63 | { |
62 | - let pool = pool.into_inner(); | |
64 | + let pool = app_data.database_pool.clone(); | |
63 | 65 | let id = id.into_inner(); |
64 | 66 | let item = item.into_inner(); |
65 | 67 | ... | ... |
server/src/uuid.rs
0 → 100644
1 | +use std::fmt::Display; | |
2 | + | |
3 | +#[derive(Clone,Copy,Debug)] | |
4 | +pub struct Uuid(pub uuid::Uuid); | |
5 | + | |
6 | +impl Display for Uuid { | |
7 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
8 | + write!(f, "{}", self.0) | |
9 | + } | |
10 | +} | |
11 | + | |
12 | +macro_rules! ns { | |
13 | + ($n:expr) => { | |
14 | + uuid::Uuid::new_v5(&uuid::Uuid::NAMESPACE_DNS, $n.as_bytes()) | |
15 | + } | |
16 | +} | |
17 | + | |
18 | +impl Uuid { | |
19 | + pub fn get(ns: &str, buf: &mut [u8]) -> Self { | |
20 | + Self(uuid::Uuid::new_v5(&ns!(ns), buf)) | |
21 | + } | |
22 | +} | ... | ... |
Please
register
or
login
to post a comment