Commit de5310cf2c8660ad8cebccb97f243d3b0d52c7d7

Authored by Georg Hopp
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.
1 CONFIG=artshop.toml 1 CONFIG=artshop.toml
2 -DATABASE_URL=crud.db 2 +#DATABASE_URL=crud.db
  3 +DATABASE_URL="mysql://artshop:123456@127.0.0.1/artshop"
@@ -4,3 +4,4 @@ Cargo.lock @@ -4,3 +4,4 @@ Cargo.lock
4 **/target 4 **/target
5 /pkg 5 /pkg
6 /static/ui/ 6 /static/ui/
  7 +/var
@@ -17,7 +17,7 @@ endef @@ -17,7 +17,7 @@ endef
17 17
18 start: 18 start:
19 systemfd --no-pid -s 0.0.0.0:3000 -- \ 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 wasm: 22 wasm:
23 $(call msg,BUILD WASM UI) 23 $(call msg,BUILD WASM UI)
@@ -35,6 +35,22 @@ run: build wasm @@ -35,6 +35,22 @@ run: build wasm
35 release: 35 release:
36 docker build -t artshop -f build/Dockerfile . 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 clean: 54 clean:
39 cargo clean 55 cargo clean
40 rm -Rf ./static/ui 56 rm -Rf ./static/ui
  1 +# This is the base namespace used for uuid generation.
  2 +namespace = "artshop.shome.steffers.org"
  3 +
1 [database] 4 [database]
2 # url = "./var/lib/artshop/database" 5 # url = "./var/lib/artshop/database"
3 6
@@ -59,3 +59,8 @@ web::block already for write actions. @@ -59,3 +59,8 @@ web::block already for write actions.
59 Simple explanation on technical terms synchronous, asynchronous, concurrent 59 Simple explanation on technical terms synchronous, asynchronous, concurrent
60 and parallel: 60 and parallel:
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') 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 --- This file should undo anything in `up.sql`  
  1 +-- This file should undo anything in `up.sql`
  2 +DROP TABLE users;
1 -- Your SQL goes here 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 name TEXT NOT NULL, 4 name TEXT NOT NULL,
5 address TEXT NOT NULL, 5 address TEXT NOT NULL,
6 date_created TEXT NOT NULL 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 VALUES 11 VALUES
12 - ("John", "123 Av Q", "Today"); 12 + ('John', '123 Av Q', 'Today');
1 -- This file should undo anything in `up.sql` 1 -- This file should undo anything in `up.sql`
2 -DROP INDEX "markdown_diffs_id";  
3 -DROP TABLE "markdown_diffs";  
4 -DROP TABLE "markdowns"; 2 +DROP TABLE markdown_diffs;
  3 +DROP TABLE markdowns;
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 name VARCHAR(256) NOT NULL, 3 name VARCHAR(256) NOT NULL,
4 content TEXT NOT NULL, 4 content TEXT NOT NULL,
5 number_of_versions INTEGER NOT NULL DEFAULT (1), 5 number_of_versions INTEGER NOT NULL DEFAULT (1),
@@ -13,7 +13,7 @@ CREATE TABLE "markdowns" ( @@ -13,7 +13,7 @@ CREATE TABLE "markdowns" (
13 -- The date_created here should be set to the value of 13 -- The date_created here should be set to the value of
14 -- markdown.date_updated when the patch was created. This diff_id 14 -- markdown.date_updated when the patch was created. This diff_id
15 -- is always current last max diff_id for given markdown_id plus 1. 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 markdown_id INTEGER NOT NULL, 17 markdown_id INTEGER NOT NULL,
18 diff_id INTEGER NOT NULL, 18 diff_id INTEGER NOT NULL,
19 diff BLOB NOT NULL, 19 diff BLOB NOT NULL,
@@ -21,84 +21,120 @@ CREATE TABLE "markdown_diffs" ( @@ -21,84 +21,120 @@ CREATE TABLE "markdown_diffs" (
21 PRIMARY KEY (markdown_id, diff_id) 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 VALUES 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 ## Links 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 [Heise.de](https://heise.de/ 'Heise.de') oder als Referenz am Ende des Textes 133 [Heise.de](https://heise.de/ 'Heise.de') oder als Referenz am Ende des Textes
99 wie diese nach [Telepolis][lnk1]. 134 wie diese nach [Telepolis][lnk1].
100 135
101 -## Bilder koennte man auch einbinden. 136 +## Bilder
  137 +---
102 138
103 Wie Links lassen sich auch Bilder wie mein 139 Wie Links lassen sich auch Bilder wie mein
104 ![Gravatar](https://www.gravatar.com/avatar/fd016c954ec4ed3a4315eeed6c8b97b8) 140 ![Gravatar](https://www.gravatar.com/avatar/fd016c954ec4ed3a4315eeed6c8b97b8)
@@ -106,36 +142,57 @@ in den Text ein. @@ -106,36 +142,57 @@ in den Text ein.
106 142
107 Im Fließtext sieht das allerdings ein bisschen dumm aus es sei denn man hat 143 Im Fließtext sieht das allerdings ein bisschen dumm aus es sei denn man hat
108 entsprechend angepasste styles. Besser scheint mir daher Bilder nur zwischen 144 entsprechend angepasste styles. Besser scheint mir daher Bilder nur zwischen
109 -Paragraphen zu plazieren. 145 +Paragraphen zu platzieren.
110 146
111 ![Gravatar](https://www.gravatar.com/avatar/fd016c954ec4ed3a4315eeed6c8b97b8) 147 ![Gravatar](https://www.gravatar.com/avatar/fd016c954ec4ed3a4315eeed6c8b97b8)
112 148
113 Etwas so wie hier. 149 Etwas so wie hier.
114 150
115 -## Tabellen sollten auch gehen... 151 +## Tabellen
  152 +---
116 153
117 Die folgenden Beispiele kommen von [markdown.land][lnk2]: 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 Man braucht sie nicht schön zu formatieren. 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 und die Spaltenausrichtung kann man auch einstellen: 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 [lnk1]: https://heise.de/tp/ 'Telepolis' 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' );
1 -- This file should undo anything in `up.sql` 1 -- This file should undo anything in `up.sql`
2 -DROP TABLE "images"; 2 +DROP TABLE images;
1 -CREATE TABLE "images" (  
2 - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 1 +CREATE TABLE images (
  2 + id INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
3 -- identical uuid means identical file. 3 -- identical uuid means identical file.
4 upload_uuid BLOB(16) UNIQUE, 4 upload_uuid BLOB(16) UNIQUE,
5 uuid BLOB(16) UNIQUE, 5 uuid BLOB(16) UNIQUE,
@@ -15,12 +15,13 @@ anyhow = "1.0" @@ -15,12 +15,13 @@ anyhow = "1.0"
15 artshop-common = { path = "../common" } 15 artshop-common = { path = "../common" }
16 async-std = "^1.10" 16 async-std = "^1.10"
17 chrono = "0.4.15" 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 diffy = "0.2" 19 diffy = "0.2"
20 dotenv = "0.15.0" 20 dotenv = "0.15.0"
21 flate2 = "^1.0" 21 flate2 = "^1.0"
22 futures = "^0.3" 22 futures = "^0.3"
23 futures-util = { version = "0", features = ["std"] } 23 futures-util = { version = "0", features = ["std"] }
  24 +image = "^0.23"
24 listenfd = "0.3" 25 listenfd = "0.3"
25 once_cell = "^1.9" 26 once_cell = "^1.9"
26 r2d2 = "0.8.9" 27 r2d2 = "0.8.9"
@@ -11,7 +11,8 @@ struct Locations { upload :String @@ -11,7 +11,8 @@ struct Locations { upload :String
11 , images :String } 11 , images :String }
12 12
13 #[derive(Debug, Deserialize)] 13 #[derive(Debug, Deserialize)]
14 -pub(crate) struct Config { database :Database 14 +pub(crate) struct Config { namespace :String
  15 + , database :Database
15 , locations :Locations } 16 , locations :Locations }
16 17
17 pub(crate) static CONFIG :Lazy<Config> = Lazy::new(|| Config::load()); 18 pub(crate) static CONFIG :Lazy<Config> = Lazy::new(|| Config::load());
@@ -34,6 +35,10 @@ impl Config { @@ -34,6 +35,10 @@ impl Config {
34 config 35 config
35 } 36 }
36 37
  38 + pub fn namespace(&self) -> &str {
  39 + self.namespace.as_str()
  40 + }
  41 +
37 pub fn upload_dir(&self) -> &str { 42 pub fn upload_dir(&self) -> &str {
38 self.locations.upload.as_str() 43 self.locations.upload.as_str()
39 } 44 }
@@ -18,17 +18,17 @@ use routes::upload::*; @@ -18,17 +18,17 @@ use routes::upload::*;
18 use actix_web::{guard, web, App, HttpResponse, HttpServer}; 18 use actix_web::{guard, web, App, HttpResponse, HttpServer};
19 use async_std::channel::Sender; 19 use async_std::channel::Sender;
20 use diesel::r2d2::{self, ConnectionManager}; 20 use diesel::r2d2::{self, ConnectionManager};
21 -use diesel::SqliteConnection; 21 +use diesel::MysqlConnection;
22 use listenfd::ListenFd; 22 use listenfd::ListenFd;
23 use std::sync::Arc; 23 use std::sync::Arc;
24 use std::ops::Deref; 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 #[derive(Clone)] 28 #[derive(Clone)]
29 pub struct AppData { 29 pub struct AppData {
30 pub database_pool: Arc<Pool>, 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 #[actix_rt::main] 34 #[actix_rt::main]
@@ -2,7 +2,7 @@ use std::sync::Arc; @@ -2,7 +2,7 @@ use std::sync::Arc;
2 2
3 use crate::error::*; 3 use crate::error::*;
4 use crate::{schema::*, Pool}; 4 use crate::{schema::*, Pool};
5 -use diesel::{Connection, insert_into}; 5 +use diesel::{Connection, insert_into, delete, update};
6 use diesel::prelude::*; 6 use diesel::prelude::*;
7 use serde::{Deserialize, Serialize}; 7 use serde::{Deserialize, Serialize};
8 8
@@ -32,16 +32,44 @@ pub struct ImageNew<'a> { @@ -32,16 +32,44 @@ pub struct ImageNew<'a> {
32 #[derive(Clone, Debug, Serialize, Deserialize, AsChangeset)] 32 #[derive(Clone, Debug, Serialize, Deserialize, AsChangeset)]
33 #[table_name = "images"] 33 #[table_name = "images"]
34 pub struct Upload { 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 pub upload_uuid :Option<Vec<u8>>, 44 pub upload_uuid :Option<Vec<u8>>,
  45 + pub uuid :Option<Vec<u8>>,
36 pub size :i32, 46 pub size :i32,
  47 + pub dim_x :Option<i32>,
  48 + pub dim_y :Option<i32>,
37 pub mime_type :String, 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 #[macro_export] 68 #[macro_export]
41 macro_rules! upload_uuid { 69 macro_rules! upload_uuid {
42 ($u:expr) => { 70 ($u:expr) => {
43 match &$u.upload_uuid { 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 None => None, 73 None => None,
46 } 74 }
47 }; 75 };
@@ -50,10 +78,10 @@ macro_rules! upload_uuid { @@ -50,10 +78,10 @@ macro_rules! upload_uuid {
50 #[macro_export] 78 #[macro_export]
51 macro_rules! upload_filename { 79 macro_rules! upload_filename {
52 ($u:expr) => { 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,3 +107,25 @@ pub(crate) fn upload( pool: Arc<Pool>
79 . first::<Image>(&db_connection) 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 use actix_web::{Error, HttpResponse, web}; 1 use actix_web::{Error, HttpResponse, web};
2 use anyhow::Result; 2 use anyhow::Result;
3 use async_std::fs::DirBuilder; 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 use crate::config::CONFIG; 8 use crate::config::CONFIG;
10 use std::convert::TryFrom; 9 use std::convert::TryFrom;
11 10
@@ -43,11 +42,13 @@ pub async fn upload( app_data :web::Data<AppData> @@ -43,11 +42,13 @@ pub async fn upload( app_data :web::Data<AppData>
43 while let Some(item) = body.next().await { 42 while let Some(item) = body.next().await {
44 output.write_all(&item?).await?; 43 output.write_all(&item?).await?;
45 } 44 }
  45 + output.flush().await.unwrap();
46 46
  47 + let pool_for_worker = pool.clone();
47 Ok( match web::block(move || image::upload(pool, upload)).await { 48 Ok( match web::block(move || image::upload(pool, upload)).await {
48 Ok(image) => { 49 Ok(image) => {
49 // TODO handle this as error response... 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 HttpResponse::Ok().json(image) 52 HttpResponse::Ok().json(image)
52 }, 53 },
53 Err(_) => HttpResponse::InternalServerError().finish() 54 Err(_) => HttpResponse::InternalServerError().finish()
1 table! { 1 table! {
2 images (id) { 2 images (id) {
3 id -> Integer, 3 id -> Integer,
4 - upload_uuid -> Nullable<Binary>,  
5 - uuid -> Nullable<Binary>, 4 + upload_uuid -> Nullable<Tinyblob>,
  5 + uuid -> Nullable<Tinyblob>,
6 size -> Integer, 6 size -> Integer,
7 dim_x -> Nullable<Integer>, 7 dim_x -> Nullable<Integer>,
8 dim_y -> Nullable<Integer>, 8 dim_y -> Nullable<Integer>,
9 - mime_type -> Text, 9 + mime_type -> Varchar,
10 date_created -> Text, 10 date_created -> Text,
11 date_updated -> Text, 11 date_updated -> Text,
12 } 12 }
13 } 13 }
14 14
15 table! { 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 markdowns (id) { 16 markdowns (id) {
26 id -> Integer, 17 id -> Integer,
27 - name -> Text, 18 + name -> Varchar,
28 content -> Text, 19 content -> Text,
29 number_of_versions -> Integer, 20 number_of_versions -> Integer,
30 date_created -> Text, 21 date_created -> Text,
@@ -33,6 +24,15 @@ table! { @@ -33,6 +24,15 @@ table! {
33 } 24 }
34 25
35 table! { 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 users (id) { 36 users (id) {
37 id -> Integer, 37 id -> Integer,
38 name -> Text, 38 name -> Text,
@@ -43,7 +43,7 @@ table! { @@ -43,7 +43,7 @@ table! {
43 43
44 allow_tables_to_appear_in_same_query!( 44 allow_tables_to_appear_in_same_query!(
45 images, 45 images,
46 - markdown_diffs,  
47 markdowns, 46 markdowns,
  47 + markdown_diffs,
48 users, 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 use futures::{ AsyncSeekExt, AsyncReadExt, FutureExt, StreamExt, select 7 use futures::{ AsyncSeekExt, AsyncReadExt, FutureExt, StreamExt, select
4 , stream::FuturesUnordered}; 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 use crate::uuid::Uuid; 11 use crate::uuid::Uuid;
8 12
9 use std::convert::TryFrom; 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 let (tx_upload_worker, rx_upload_worker) 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 actix_rt::spawn(async move { 20 actix_rt::spawn(async move {
17 let mut workers = FuturesUnordered::new(); 21 let mut workers = FuturesUnordered::new();
@@ -21,7 +25,7 @@ pub fn launch() -> Sender<Image> { @@ -21,7 +25,7 @@ pub fn launch() -> Sender<Image> {
21 image = rx_upload_worker.recv().fuse() => { 25 image = rx_upload_worker.recv().fuse() => {
22 match image { 26 match image {
23 Err(_) => break, 27 Err(_) => break,
24 - Ok(image) => workers.push(worker(image)), 28 + Ok((pool, image)) => workers.push(worker(pool, image)),
25 } 29 }
26 }, 30 },
27 _result = workers.next() => {}, 31 _result = workers.next() => {},
@@ -37,16 +41,50 @@ pub fn launch() -> Sender<Image> { @@ -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 let upload_filename = upload_filename!(image).unwrap(); 45 let upload_filename = upload_filename!(image).unwrap();
42 let mut f = File::open(&upload_filename).await.unwrap(); 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 get_sample(&mut f, buf.as_mut()).await.unwrap(); 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 async fn read_at( f :&mut File 90 async fn read_at( f :&mut File
Please register or login to post a comment