Commit de5310cf2c8660ad8cebccb97f243d3b0d52c7d7
1 parent
ecfa39ac
Fix #5 - Use mysql/mariadb now and finish upload
Because the sqlite was no longer able to handle the concurrency even one user produces during a multi- file- upload.
Showing
18 changed files
with
303 additions
and
125 deletions
... | ... | @@ -17,7 +17,7 @@ endef |
17 | 17 | |
18 | 18 | start: |
19 | 19 | systemfd --no-pid -s 0.0.0.0:3000 -- \ |
20 | - cargo watch -i static/ -s "PROFILE=$(PROFILE) make run" | |
20 | + cargo watch -i static/ -i var/ -s "PROFILE=$(PROFILE) make run" | |
21 | 21 | |
22 | 22 | wasm: |
23 | 23 | $(call msg,BUILD WASM UI) |
... | ... | @@ -35,6 +35,22 @@ run: build wasm |
35 | 35 | release: |
36 | 36 | docker build -t artshop -f build/Dockerfile . |
37 | 37 | |
38 | +devdb: | |
39 | + docker network create mariadb-dev-network | |
40 | + docker run --detach --network mariadb-dev-network --name mariadb-dev \ | |
41 | + -p 3306:3306 \ | |
42 | + --env MARIADB_USER=artshop \ | |
43 | + --env MARIADB_PASSWORD=123456 \ | |
44 | + --env MARIADB_ROOT_PASSWORD=123456 mariadb:latest | |
45 | + | |
46 | +enterdb: | |
47 | + docker exec -it mariadb-dev mysql -D artshop -u artshop -p | |
48 | +# docker run -it --network mariadb-dev-network --rm mariadb:latest \ | |
49 | +# mysql -h mariadb-dev -u artshop -p | |
50 | + | |
51 | +rootdb: | |
52 | + docker exec -it mariadb-dev mysql -p | |
53 | + | |
38 | 54 | clean: |
39 | 55 | cargo clean |
40 | 56 | rm -Rf ./static/ui | ... | ... |
... | ... | @@ -59,3 +59,8 @@ web::block already for write actions. |
59 | 59 | Simple explanation on technical terms synchronous, asynchronous, concurrent |
60 | 60 | and parallel: |
61 | 61 | [synchronous vs. asynchronous vs. concurrent vs. parallel](https://medium.com/plain-and-simple/synchronous-vs-asynchronous-vs-concurrent-vs-parallel-4342bfb8b9f2, 'synchronous vs. asynchronous vs. concurrent vs. parallel') |
62 | + | |
63 | +# Docker mariadb preparation | |
64 | + | |
65 | + GRANT ALL PRIVILEGES ON artshop.* TO 'artshop'@'%'; | |
66 | + CREATE DATABASE artshop CHARACTER SET = 'utf8mb3' COLLATE = 'utf8mb3_general_ci'; | ... | ... |
1 | 1 | -- Your SQL goes here |
2 | -CREATE TABLE "users" ( | |
3 | - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | |
2 | +CREATE TABLE users ( | |
3 | + id INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, | |
4 | 4 | name TEXT NOT NULL, |
5 | 5 | address TEXT NOT NULL, |
6 | 6 | date_created TEXT NOT NULL |
7 | 7 | ); |
8 | 8 | |
9 | -INSERT INTO | |
10 | - "users"(name, address, date_created) | |
9 | +INSERT INTO users | |
10 | + (name, address, date_created) | |
11 | 11 | VALUES |
12 | - ("John", "123 Av Q", "Today"); | |
12 | + ('John', '123 Av Q', 'Today'); | ... | ... |
1 | -CREATE TABLE "markdowns" ( | |
2 | - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | |
1 | +CREATE TABLE markdowns ( | |
2 | + id INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, | |
3 | 3 | name VARCHAR(256) NOT NULL, |
4 | 4 | content TEXT NOT NULL, |
5 | 5 | number_of_versions INTEGER NOT NULL DEFAULT (1), |
... | ... | @@ -13,7 +13,7 @@ CREATE TABLE "markdowns" ( |
13 | 13 | -- The date_created here should be set to the value of |
14 | 14 | -- markdown.date_updated when the patch was created. This diff_id |
15 | 15 | -- is always current last max diff_id for given markdown_id plus 1. |
16 | -CREATE TABLE "markdown_diffs" ( | |
16 | +CREATE TABLE markdown_diffs ( | |
17 | 17 | markdown_id INTEGER NOT NULL, |
18 | 18 | diff_id INTEGER NOT NULL, |
19 | 19 | diff BLOB NOT NULL, |
... | ... | @@ -21,84 +21,120 @@ CREATE TABLE "markdown_diffs" ( |
21 | 21 | PRIMARY KEY (markdown_id, diff_id) |
22 | 22 | ); |
23 | 23 | |
24 | -INSERT INTO | |
25 | - "markdowns"(name, content, date_created, date_updated) | |
24 | +INSERT INTO markdowns | |
25 | + (name, content, date_created, date_updated) | |
26 | 26 | VALUES |
27 | - ( "md-example" | |
28 | - , "# Ein sehr schöner Titel | |
27 | + ( 'md-example' | |
28 | + , "# Markdown Cheatsheat | |
29 | 29 | |
30 | -## Ein sinnloser Text | |
30 | +## Überschriften | |
31 | +--- | |
31 | 32 | |
32 | -Hier kommt ganz viel Text der irgendwie auch was machen soll, aber Zeilen | |
33 | -sollen auch im <pre> Eingabefeld automatisch umbrechen. | |
33 | +# # <H1> | |
34 | +## ## <H2> | |
35 | +### ### <H3> | |
36 | +#### #### <H4> | |
37 | +##### ##### <H5> | |
38 | +###### ###### <H6> | |
34 | 39 | |
35 | -Ein neuner Paragraph beginnt nach einer Leerzeile. | |
36 | -Ein Umbruch entsteht wie gewohnt durch 2 spaces am Ende einer | |
37 | -Zeile. | |
40 | +## Absätze und Umbrüche | |
41 | +--- | |
38 | 42 | |
39 | -## Fußnoten | |
43 | +Ein einfacher Zeilenumbruch | |
44 | +verändert den Textfluss nicht. Spaces haben auch keinen Einfluss auf den Textfluss. Es ist selten eine gute Idee große Abstände innerhalb eines Textes zu habe, sollte man diese aber wirklich brauchen kann man auf inline html zurückgreifen. | |
40 | 45 | |
41 | -Vllt. kann man sogar so was wie Fussnoten[^1] in den Markdown Text | |
42 | -einbinden... diese kann man dann irgendwo einbauen... | |
46 | +Leerzeilen erzeugen neue Paragraphen. Um im formatierten Text einen Zeilenumbruch zu erzeugen verwendet man zwei Spaces vor einem Zeilenumbruch im Eingabetext. | |
47 | +Dies führt nicht zu einem Paragraphen. | |
43 | 48 | |
49 | +## Hervorhebungen | |
44 | 50 | --- |
45 | 51 | |
46 | -[^1]: Zum Beispiel so... | |
47 | - | |
48 | -[^2]: Oder so... | |
49 | - | |
50 | -## inline html ist im Moment auch ok. | |
52 | +- *kursive (schwache) Hervorhebung* | |
53 | +- **fette (starke) Hervorhebung** | |
54 | +- ***kursiv und fette (sehr starke) Hervorhebung*** | |
55 | +- *schwache mit **eingebetteter starker** Hervorhebung* | |
56 | +- **starke mit *eingebetteter schwacher* Hervorhebung** | |
57 | +- ~~durchgestrichen~~ | |
58 | +- <u>unterstreichen nur mit HTML</u> | |
59 | +- ~~*durchgestrichen kursiv*~~ | |
60 | +- **~~fett durchgestrichen~~** | |
61 | +- *<u>kursiv unterstrichen</u>* | |
62 | +- <u>**unterstrichen fett**</u> | |
63 | + | |
64 | +## Gedanken- und Binde--strich | |
65 | +--- | |
51 | 66 | |
52 | -<pre>Lustigerweise geht auch inline html</pre> | |
67 | +Ein Einfaches Minus bildet im Text den Gedankenstrich *hyphen* (-). Zwei oder mehr Minus werden zu verschieden langen Bindestrichen *dash* (--, ---). In regulärem | |
68 | +Text kommen zwei Minuszeichen hintereinander in der Regel nicht vor. In | |
69 | +Programmcode, der in ASCII geschrieben ist allerdings schon. Will man also zwei | |
70 | +Minuszeichen separat darstellen kann man diese in inline code packen (`--, ---`). | |
53 | 71 | |
54 | -## Listen for fun | |
72 | +## Listen | |
73 | +--- | |
55 | 74 | |
56 | -- ein Liste | |
57 | - - mehr Liste | |
58 | - - diesmal als Subliste. | |
59 | -- und was auch immer... | |
60 | - 1. und nun Verschachtelt. | |
61 | - 1. Numeriert. | |
62 | - 2. huhuhu | |
63 | - 3. wie bitte. | |
64 | - 2. juhu | |
65 | -- noch mehr Liste | |
75 | +- erster Listeneintrag | |
76 | + - erster Unterlisteneintrag | |
77 | + - zweiter Unterlisteneintrag | |
78 | +- zweiter Listeneintrag | |
79 | + 1. erster nummerierter Listeneintrag | |
80 | + 1. erster nummerierter Unterlisteneintrag | |
81 | + 2. zweiter nummerierter Unterlisteneintrag | |
82 | + 3. dritter nummerierter Unterlisteneintrag | |
83 | + 2. zweiter nummerierter Listeneintrag | |
84 | +- dritter Listeneintrag | |
85 | + - [x] erster Auswahllisteneintrag | |
86 | + - [ ] zweiter Auswahllisteneintrag | |
87 | + - [x] dritter Auswahllisteneintrag | |
88 | +- vierter Listeneintrag | |
89 | + 1. [ ] erster nummerierter Auswahllisteneintrag | |
90 | + 2. [x] zweiter nummerierter Auswahllisteneintrag | |
91 | + | |
92 | +## Code Blöcke | |
93 | +--- | |
66 | 94 | |
67 | -## Preformated Text | |
95 | + Dies ist ein codeblock durch Einrückung. | |
96 | + In diesem werden keine Formatierungen | |
97 | + vorgenommen. | |
68 | 98 | |
69 | -```Hier kommt der code``` | |
99 | +Mit backticks lassen sich Codeblöcke mit Sprachinformation | |
100 | +erstellen. Theoretisch könnte für solche Böcke dann Syntax-Highliting eingebaut werden. | |
70 | 101 | |
71 | -Und hier der Paragraph mit `inline code` der auch sehr schön aussehen kann. | |
102 | +```shell | |
103 | +#!/bin/env sh | |
72 | 104 | |
73 | -## Hervorhebungen | |
105 | +FOO="foo" | |
74 | 106 | |
75 | -Man kann Text auch sehr schön formatieren. So ist es z.B. möglich | |
76 | -*Worte kursiv zu stellen* oder man kann **sie auch fett schreiben**. | |
77 | -Als spezielles feature kann der von mir verwendete Parser auch | |
78 | -~~Texte durchstreichen~~. | |
107 | +function func() { | |
108 | + local BAR=bar | |
109 | +} | |
110 | +``` | |
79 | 111 | |
80 | -Nur wenn man Text <u>unterstreichen</u> will muss man auf inline html | |
81 | -zurückgreifen. | |
112 | +Auch in den Fließtext lassen sich `inline code` Elemente einfügen um z.B. einzelne Kommandos hervorzuheben. | |
82 | 113 | |
83 | -## Blockquotes und horizontale Linie | |
114 | +## Zitate und horizontale Linie | |
115 | +--- | |
84 | 116 | |
85 | -> Dies sollte jetzt als quote erkennbar sein. | |
117 | +> Dies ist ein Zitat. | |
86 | 118 | > |
87 | ->> Auch diese sind schachtelbar | |
119 | +>> Zitate können verschachtelt sein. | |
88 | 120 | > |
89 | -> Und weiter gehts. | |
121 | +> Wir können also zitieren was jemand zitiert hat. | |
122 | +> Solange die Zeilen ohne Unterbrechung mit einem > | |
123 | +> beginnen bleibt es ein Zitat | |
90 | 124 | |
91 | ---- | |
92 | 125 | |
93 | -> Aber dies ist ein neuer quote. | |
126 | +> Sobald eine Zeile ohne führendes > auftaucht endet ein | |
127 | +> Zitat. | |
94 | 128 | |
95 | 129 | ## Links |
130 | +--- | |
96 | 131 | |
97 | -Ein link kann inline geschrieben werden, so wie diese zu | |
132 | +Ein Link kann inline geschrieben werden, so wie diese zu | |
98 | 133 | [Heise.de](https://heise.de/ 'Heise.de') oder als Referenz am Ende des Textes |
99 | 134 | wie diese nach [Telepolis][lnk1]. |
100 | 135 | |
101 | -## Bilder koennte man auch einbinden. | |
136 | +## Bilder | |
137 | +--- | |
102 | 138 | |
103 | 139 | Wie Links lassen sich auch Bilder wie mein |
104 | 140 | ![Gravatar](https://www.gravatar.com/avatar/fd016c954ec4ed3a4315eeed6c8b97b8) |
... | ... | @@ -106,36 +142,57 @@ in den Text ein. |
106 | 142 | |
107 | 143 | Im Fließtext sieht das allerdings ein bisschen dumm aus es sei denn man hat |
108 | 144 | entsprechend angepasste styles. Besser scheint mir daher Bilder nur zwischen |
109 | -Paragraphen zu plazieren. | |
145 | +Paragraphen zu platzieren. | |
110 | 146 | |
111 | 147 | ![Gravatar](https://www.gravatar.com/avatar/fd016c954ec4ed3a4315eeed6c8b97b8) |
112 | 148 | |
113 | 149 | Etwas so wie hier. |
114 | 150 | |
115 | -## Tabellen sollten auch gehen... | |
151 | +## Tabellen | |
152 | +--- | |
116 | 153 | |
117 | 154 | Die folgenden Beispiele kommen von [markdown.land][lnk2]: |
118 | 155 | |
119 | -| Item | Price | # In stock | | |
120 | -|--------------|-----------|------------| | |
121 | -| Juicy Apples | 1.99 | *8* | | |
122 | -| Bananas | **1.89** | 5234 | | |
156 | +| Artikel | Preis | Bestand | | |
157 | +|---------------|-----------|------------| | |
158 | +| Saftige Äpfel | 1.99 | *8* | | |
159 | +| Bananen | **1.89** | 5234 | | |
123 | 160 | |
124 | 161 | Man braucht sie nicht schön zu formatieren. |
125 | 162 | |
126 | -Item | Price | # In stock | |
163 | +Artikel | Preis | Bestand | |
127 | 164 | ---|---|--- |
128 | -Juicy Apples | 1.99 | 739 | |
129 | -Bananas | 1.89 | 6 | |
165 | +Saftige Äpfel | 1.99 | 739 | |
166 | +Bananen | 1.89 | 6 | |
130 | 167 | |
131 | 168 | und die Spaltenausrichtung kann man auch einstellen: |
132 | 169 | |
133 | -| Item | Price | # In stock | | |
134 | -|--------------|:-----:|-----------:| | |
135 | -| Juicy Apples | 1.99 | 739 | | |
136 | -| Bananas | 1.8900 | 6 | | |
170 | +| Artikel | Preis | Bestand | | |
171 | +|---------------|:-------:|-----------:| | |
172 | +| Saftige Äpfel | 1.99 | 739 | | |
173 | +| Bananen | 1.8900 | 6 | | |
174 | + | |
175 | +## Fußnoten | |
176 | +--- | |
177 | + | |
178 | +Man kann auch verlinkte Fußnoten[^1] in den Text | |
179 | +einbinden. Die Fußnote selber kann dann an beliebige stelle im Text stehen. | |
180 | + | |
181 | +--- | |
182 | +[^1]: Zum Beispiel so. | |
183 | + | |
184 | +[^2]: Diese Fußnote hat keine Verlinkung im Text. | |
185 | + | |
186 | +## HTML einbetten | |
187 | + | |
188 | +<ul> | |
189 | +<li> | |
190 | +<pre>Man kann auch direkt HTML tags einbetten, | |
191 | +wie hier.</pre> | |
192 | +</li> | |
193 | +</ul> | |
137 | 194 | |
138 | 195 | [lnk1]: https://heise.de/tp/ 'Telepolis' |
139 | -[lnk2]: https://markdown.land/markdown-table 'markdown.land'" | |
140 | - , "Today" | |
141 | - , "Today" ); | |
196 | +[lnk2]: https://arkdown.land/markdown-table 'markdown.land' " | |
197 | + , '2022-01-29 21:33:34.000' | |
198 | + , '2022-01-29 21:33:34.000' ); | ... | ... |
... | ... | @@ -15,12 +15,13 @@ anyhow = "1.0" |
15 | 15 | artshop-common = { path = "../common" } |
16 | 16 | async-std = "^1.10" |
17 | 17 | chrono = "0.4.15" |
18 | -diesel = { version = "1.4.7", features = ["sqlite", "r2d2"]} | |
18 | +diesel = { version = "1.4.7", features = ["mysql", "sqlite", "r2d2"]} | |
19 | 19 | diffy = "0.2" |
20 | 20 | dotenv = "0.15.0" |
21 | 21 | flate2 = "^1.0" |
22 | 22 | futures = "^0.3" |
23 | 23 | futures-util = { version = "0", features = ["std"] } |
24 | +image = "^0.23" | |
24 | 25 | listenfd = "0.3" |
25 | 26 | once_cell = "^1.9" |
26 | 27 | r2d2 = "0.8.9" | ... | ... |
... | ... | @@ -11,7 +11,8 @@ struct Locations { upload :String |
11 | 11 | , images :String } |
12 | 12 | |
13 | 13 | #[derive(Debug, Deserialize)] |
14 | -pub(crate) struct Config { database :Database | |
14 | +pub(crate) struct Config { namespace :String | |
15 | + , database :Database | |
15 | 16 | , locations :Locations } |
16 | 17 | |
17 | 18 | pub(crate) static CONFIG :Lazy<Config> = Lazy::new(|| Config::load()); |
... | ... | @@ -34,6 +35,10 @@ impl Config { |
34 | 35 | config |
35 | 36 | } |
36 | 37 | |
38 | + pub fn namespace(&self) -> &str { | |
39 | + self.namespace.as_str() | |
40 | + } | |
41 | + | |
37 | 42 | pub fn upload_dir(&self) -> &str { |
38 | 43 | self.locations.upload.as_str() |
39 | 44 | } | ... | ... |
... | ... | @@ -18,17 +18,17 @@ use routes::upload::*; |
18 | 18 | use actix_web::{guard, web, App, HttpResponse, HttpServer}; |
19 | 19 | use async_std::channel::Sender; |
20 | 20 | use diesel::r2d2::{self, ConnectionManager}; |
21 | -use diesel::SqliteConnection; | |
21 | +use diesel::MysqlConnection; | |
22 | 22 | use listenfd::ListenFd; |
23 | 23 | use std::sync::Arc; |
24 | 24 | use std::ops::Deref; |
25 | 25 | |
26 | -pub(crate) type Pool = r2d2::Pool<ConnectionManager<SqliteConnection>>; | |
26 | +pub(crate) type Pool = r2d2::Pool<ConnectionManager<MysqlConnection>>; | |
27 | 27 | |
28 | 28 | #[derive(Clone)] |
29 | 29 | pub struct AppData { |
30 | 30 | pub database_pool: Arc<Pool>, |
31 | - pub tx_upload_worker: Sender<Image>, | |
31 | + pub tx_upload_worker: Sender<(Arc<Pool>, Image)>, | |
32 | 32 | } |
33 | 33 | |
34 | 34 | #[actix_rt::main] | ... | ... |
... | ... | @@ -2,7 +2,7 @@ use std::sync::Arc; |
2 | 2 | |
3 | 3 | use crate::error::*; |
4 | 4 | use crate::{schema::*, Pool}; |
5 | -use diesel::{Connection, insert_into}; | |
5 | +use diesel::{Connection, insert_into, delete, update}; | |
6 | 6 | use diesel::prelude::*; |
7 | 7 | use serde::{Deserialize, Serialize}; |
8 | 8 | |
... | ... | @@ -32,16 +32,44 @@ pub struct ImageNew<'a> { |
32 | 32 | #[derive(Clone, Debug, Serialize, Deserialize, AsChangeset)] |
33 | 33 | #[table_name = "images"] |
34 | 34 | pub struct Upload { |
35 | + pub upload_uuid :Option<Vec<u8>>, | |
36 | + pub size :i32, | |
37 | + pub mime_type :String, | |
38 | +} | |
39 | + | |
40 | +#[derive(Clone, Debug, Serialize, Deserialize, AsChangeset)] | |
41 | +#[table_name = "images"] | |
42 | +#[changeset_options(treat_none_as_null = "true")] | |
43 | +pub struct ImagePatch { | |
35 | 44 | pub upload_uuid :Option<Vec<u8>>, |
45 | + pub uuid :Option<Vec<u8>>, | |
36 | 46 | pub size :i32, |
47 | + pub dim_x :Option<i32>, | |
48 | + pub dim_y :Option<i32>, | |
37 | 49 | pub mime_type :String, |
50 | + pub date_updated :String | |
51 | +} | |
52 | + | |
53 | +impl From<Image> for ImagePatch { | |
54 | + fn from(image: Image) -> Self { | |
55 | + let now = chrono::Local::now().naive_local(); | |
56 | + | |
57 | + Self { upload_uuid :image.upload_uuid | |
58 | + , uuid :image.uuid | |
59 | + , size :image.size | |
60 | + , dim_x :image.dim_x | |
61 | + , dim_y :image.dim_y | |
62 | + , mime_type :image.mime_type | |
63 | + , date_updated :format!("{}", now) | |
64 | + } | |
65 | + } | |
38 | 66 | } |
39 | 67 | |
40 | 68 | #[macro_export] |
41 | 69 | macro_rules! upload_uuid { |
42 | 70 | ($u:expr) => { |
43 | 71 | match &$u.upload_uuid { |
44 | - Some(uuid) => Uuid::try_from(uuid.as_slice()).ok(), | |
72 | + Some(uuid) => $crate::uuid::Uuid::try_from(uuid.as_slice()).ok(), | |
45 | 73 | None => None, |
46 | 74 | } |
47 | 75 | }; |
... | ... | @@ -50,10 +78,10 @@ macro_rules! upload_uuid { |
50 | 78 | #[macro_export] |
51 | 79 | macro_rules! upload_filename { |
52 | 80 | ($u:expr) => { |
53 | - upload_uuid!($u).and_then(|uuid| | |
54 | - Some(format!( "{}/upload_{}" | |
55 | - , CONFIG.upload_dir() | |
56 | - , uuid))) | |
81 | + $crate::upload_uuid!($u) | |
82 | + . and_then(|uuid| Some(format!( "{}/upload_{}" | |
83 | + , $crate::config::CONFIG.upload_dir() | |
84 | + , uuid ))) | |
57 | 85 | }; |
58 | 86 | } |
59 | 87 | |
... | ... | @@ -79,3 +107,25 @@ pub(crate) fn upload( pool: Arc<Pool> |
79 | 107 | . first::<Image>(&db_connection) |
80 | 108 | })?) |
81 | 109 | } |
110 | + | |
111 | +pub(crate) fn finalize( pool: Arc<Pool> | |
112 | + , item: Image ) -> Result<Image> { | |
113 | + use crate::schema::images::dsl::*; | |
114 | + | |
115 | + let db_connection = pool.get()?; | |
116 | + let item_uuid = item.uuid.clone(); | |
117 | + | |
118 | + match images . filter(uuid.eq(item_uuid)) | |
119 | + . first::<Image>(&db_connection) { | |
120 | + Ok(image) => { | |
121 | + delete(images.find(item.id)).execute(&db_connection)?; | |
122 | + Ok(image) | |
123 | + }, | |
124 | + Err(_) => { | |
125 | + let image = images.find(item.id); | |
126 | + let patch = ImagePatch::from(item.clone()); | |
127 | + update(image).set(&patch).execute(&db_connection)?; | |
128 | + Ok(item) | |
129 | + }, | |
130 | + } | |
131 | +} | ... | ... |
1 | 1 | use actix_web::{Error, HttpResponse, web}; |
2 | 2 | use anyhow::Result; |
3 | 3 | use async_std::fs::DirBuilder; |
4 | -use futures::stream::StreamExt; | |
5 | -use async_std::{fs::OpenOptions, io::WriteExt}; | |
6 | -use crate::uuid::Uuid; | |
4 | +use futures::{stream::StreamExt, AsyncWriteExt}; | |
5 | +use async_std::fs::OpenOptions; | |
7 | 6 | |
8 | -use crate::{AppData, models::image::{Upload, self}, upload_filename, upload_uuid}; | |
7 | +use crate::{AppData, models::image::{Upload, self}, upload_filename}; | |
9 | 8 | use crate::config::CONFIG; |
10 | 9 | use std::convert::TryFrom; |
11 | 10 | |
... | ... | @@ -43,11 +42,13 @@ pub async fn upload( app_data :web::Data<AppData> |
43 | 42 | while let Some(item) = body.next().await { |
44 | 43 | output.write_all(&item?).await?; |
45 | 44 | } |
45 | + output.flush().await.unwrap(); | |
46 | 46 | |
47 | + let pool_for_worker = pool.clone(); | |
47 | 48 | Ok( match web::block(move || image::upload(pool, upload)).await { |
48 | 49 | Ok(image) => { |
49 | 50 | // TODO handle this as error response... |
50 | - worker.send(image.clone()).await.unwrap(); | |
51 | + worker.send((pool_for_worker, image.clone())).await.unwrap(); | |
51 | 52 | HttpResponse::Ok().json(image) |
52 | 53 | }, |
53 | 54 | Err(_) => HttpResponse::InternalServerError().finish() | ... | ... |
1 | 1 | table! { |
2 | 2 | images (id) { |
3 | 3 | id -> Integer, |
4 | - upload_uuid -> Nullable<Binary>, | |
5 | - uuid -> Nullable<Binary>, | |
4 | + upload_uuid -> Nullable<Tinyblob>, | |
5 | + uuid -> Nullable<Tinyblob>, | |
6 | 6 | size -> Integer, |
7 | 7 | dim_x -> Nullable<Integer>, |
8 | 8 | dim_y -> Nullable<Integer>, |
9 | - mime_type -> Text, | |
9 | + mime_type -> Varchar, | |
10 | 10 | date_created -> Text, |
11 | 11 | date_updated -> Text, |
12 | 12 | } |
13 | 13 | } |
14 | 14 | |
15 | 15 | table! { |
16 | - markdown_diffs (markdown_id, diff_id) { | |
17 | - markdown_id -> Integer, | |
18 | - diff_id -> Integer, | |
19 | - diff -> Binary, | |
20 | - date_created -> Text, | |
21 | - } | |
22 | -} | |
23 | - | |
24 | -table! { | |
25 | 16 | markdowns (id) { |
26 | 17 | id -> Integer, |
27 | - name -> Text, | |
18 | + name -> Varchar, | |
28 | 19 | content -> Text, |
29 | 20 | number_of_versions -> Integer, |
30 | 21 | date_created -> Text, |
... | ... | @@ -33,6 +24,15 @@ table! { |
33 | 24 | } |
34 | 25 | |
35 | 26 | table! { |
27 | + markdown_diffs (markdown_id, diff_id) { | |
28 | + markdown_id -> Integer, | |
29 | + diff_id -> Integer, | |
30 | + diff -> Blob, | |
31 | + date_created -> Text, | |
32 | + } | |
33 | +} | |
34 | + | |
35 | +table! { | |
36 | 36 | users (id) { |
37 | 37 | id -> Integer, |
38 | 38 | name -> Text, |
... | ... | @@ -43,7 +43,7 @@ table! { |
43 | 43 | |
44 | 44 | allow_tables_to_appear_in_same_query!( |
45 | 45 | images, |
46 | - markdown_diffs, | |
47 | 46 | markdowns, |
47 | + markdown_diffs, | |
48 | 48 | users, |
49 | 49 | ); | ... | ... |
1 | -use std::io::SeekFrom; | |
2 | -use async_std::{fs::File, channel::{Sender, Receiver, bounded}}; | |
1 | +use std::{io::{SeekFrom, ErrorKind}, sync::Arc}; | |
2 | +use actix_web::web; | |
3 | +use async_std::{ fs::{File, DirBuilder, copy, metadata, remove_file} | |
4 | + , channel::{Sender, Receiver, bounded} | |
5 | + , path::PathBuf | |
6 | + , io::Result }; | |
3 | 7 | use futures::{ AsyncSeekExt, AsyncReadExt, FutureExt, StreamExt, select |
4 | 8 | , stream::FuturesUnordered}; |
5 | 9 | |
6 | -use crate::{models::image::Image, upload_filename, upload_uuid}; | |
10 | +use crate::{models::image::{Image, finalize}, upload_filename, config::CONFIG, Pool}; | |
7 | 11 | use crate::uuid::Uuid; |
8 | 12 | |
9 | 13 | use std::convert::TryFrom; |
10 | -use crate::config::CONFIG; | |
14 | +use image::{io::Reader as ImageReader, GenericImageView}; | |
11 | 15 | |
12 | -pub fn launch() -> Sender<Image> { | |
16 | +pub fn launch() -> Sender<(Arc<Pool>, Image)> { | |
13 | 17 | let (tx_upload_worker, rx_upload_worker) |
14 | - : (Sender<Image>, Receiver<Image>) = bounded(32); | |
18 | + : (Sender<(Arc<Pool>, Image)>, Receiver<(Arc<Pool>, Image)>) = bounded(32); | |
15 | 19 | |
16 | 20 | actix_rt::spawn(async move { |
17 | 21 | let mut workers = FuturesUnordered::new(); |
... | ... | @@ -21,7 +25,7 @@ pub fn launch() -> Sender<Image> { |
21 | 25 | image = rx_upload_worker.recv().fuse() => { |
22 | 26 | match image { |
23 | 27 | Err(_) => break, |
24 | - Ok(image) => workers.push(worker(image)), | |
28 | + Ok((pool, image)) => workers.push(worker(pool, image)), | |
25 | 29 | } |
26 | 30 | }, |
27 | 31 | _result = workers.next() => {}, |
... | ... | @@ -37,16 +41,50 @@ pub fn launch() -> Sender<Image> { |
37 | 41 | } |
38 | 42 | |
39 | 43 | |
40 | -async fn worker(image :Image) { | |
44 | +async fn worker(pool :Arc<Pool>, mut image :Image) { | |
41 | 45 | let upload_filename = upload_filename!(image).unwrap(); |
42 | 46 | let mut f = File::open(&upload_filename).await.unwrap(); |
43 | - let mut buf = vec!['.' as u8; 3 * 4096]; | |
47 | + | |
48 | + let mut buf = vec!['.' as u8; 3 * 3 * 4096]; | |
44 | 49 | get_sample(&mut f, buf.as_mut()).await.unwrap(); |
45 | - println!( "[upload worker] filename: {}" | |
46 | - , upload_filename ); | |
47 | - println!( "[upload worker] uuid: {}" | |
48 | - , Uuid::get( "some.unique.namespace" | |
49 | - , buf.as_mut() ) ); | |
50 | + let uuid = Uuid::get(CONFIG.namespace(), buf.as_mut()); | |
51 | + let uuid_string = format!("{}", uuid); | |
52 | + | |
53 | + let mut image_path = PathBuf::from(CONFIG.images_dir()); | |
54 | + image_path.push(&uuid_string.as_str()[..2]); | |
55 | + image_path.push(&uuid_string.as_str()[..5]); | |
56 | + | |
57 | + DirBuilder::new() . recursive(true) | |
58 | + . create(&image_path) | |
59 | + . await | |
60 | + . unwrap(); | |
61 | + | |
62 | + image_path.push(&uuid_string); | |
63 | + | |
64 | + image.upload_uuid = None; | |
65 | + image.uuid = Some(uuid.0.as_bytes().to_vec()); | |
66 | + | |
67 | + match metadata(&image_path).await { | |
68 | + Err(e) if e.kind() == ErrorKind::NotFound => { | |
69 | + copy(&upload_filename, &image_path).await.unwrap(); | |
70 | + | |
71 | + let img = ImageReader::open(&image_path).unwrap() | |
72 | + . with_guessed_format().unwrap() | |
73 | + . decode().unwrap(); | |
74 | + let (dim_x, dim_y) = img.dimensions(); | |
75 | + | |
76 | + image.dim_x = Some(dim_x as i32); | |
77 | + image.dim_y = Some(dim_y as i32); | |
78 | + }, | |
79 | + Err(e) => { | |
80 | + let e :Result<()> = Err(e); | |
81 | + e.unwrap(); | |
82 | + }, | |
83 | + Ok(_) => {}, | |
84 | + } | |
85 | + | |
86 | + remove_file(&upload_filename).await.unwrap(); | |
87 | + web::block(move || finalize(pool, image)).await.unwrap(); | |
50 | 88 | } |
51 | 89 | |
52 | 90 | async fn read_at( f :&mut File | ... | ... |
Please
register
or
login
to post a comment