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 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 4 **/target
5 5 /pkg
6 6 /static/ui/
  7 +/var
... ...
... ... @@ -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
... ...
  1 +# This is the base namespace used for uuid generation.
  2 +namespace = "artshop.shome.steffers.org"
  3 +
1 4 [database]
2 5 # url = "./var/lib/artshop/database"
3 6
... ...
... ... @@ -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   --- This file should undo anything in `up.sql`
\ No newline at end of file
  1 +-- This file should undo anything in `up.sql`
  2 +DROP TABLE users;
... ...
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 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 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' );
... ...
1 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 3 -- identical uuid means identical file.
4 4 upload_uuid BLOB(16) UNIQUE,
5 5 uuid BLOB(16) UNIQUE,
... ...
... ... @@ -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