Some queries and models

This commit is contained in:
Raphaël Thériault 2020-06-24 14:03:05 -04:00
parent 10d063c0da
commit 013c3f5ca8
5 changed files with 295 additions and 15 deletions

29
Cargo.lock generated
View File

@ -285,6 +285,8 @@ dependencies = [
"anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -376,6 +378,17 @@ name = "futures-io"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-macro"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-sink"
version = "0.3.5"
@ -397,11 +410,14 @@ dependencies = [
"futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-project 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -959,6 +975,16 @@ dependencies = [
"version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro-nested"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "1.0.18"
@ -1860,6 +1886,7 @@ dependencies = [
"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
"checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
@ -1925,6 +1952,8 @@ dependencies = [
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678"
"checksum proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53"
"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
"checksum proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"

View File

@ -18,16 +18,19 @@ license = "MIT"
anyhow = "1.0.31"
cfg-if = "0.1.10"
chrono = "0.4.11"
futures-core = "0.3.5"
futures-util = "0.3.5"
log = { version = "0.4.8", features = ["serde"] }
rust-argon2 = "0.8.2"
serde = { version = "1.0.114", features = ["derive"] }
serde_json = "1.0.55"
sqlx = { version = "0.3.5", features = ["chrono", "runtime-tokio"], default-features = false }
sqlx = { version = "0.3.5", features = ["chrono", "macros", "runtime-tokio"], default-features = false }
structopt = "0.3.15"
tokio = { version = "0.2.21", features = ["blocking", "fs", "rt-core"] }
warp = { version = "0.2.3", features = ["multipart"], default-features = false }
# Makes cross-compiliing easier by statically linking to OpenSSL
[target.'cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))'.dependencies]
openssl-sys = { version = "0.9", features = ["vendored"] }
[features]

View File

@ -1 +1,111 @@
pub mod models;
pub mod pool;
use anyhow::Error;
use futures_core::Stream;
use futures_util::StreamExt;
use models::{Filite, FiliteRow, User, UserRow};
use pool::Pool;
use std::{convert::TryInto, pin::Pin};
pub async fn fetch(id: &str, pool: &Pool) -> Result<Option<Filite>, Error> {
let sql = "SELECT * FROM data WHERE id = ?";
let row: Option<FiliteRow> = match pool {
#[cfg(feature = "sqlite")]
Pool::Sqlite(p) => {
use sqlx::sqlite::SqliteQueryAs;
sqlx::query_as(sql).bind(id).fetch_optional(p).await?
}
#[cfg(feature = "postgres")]
Pool::Postgres(p) => {
use sqlx::postgres::PgQueryAs;
sqlx::query_as(sql).bind(id).fetch_optional(p).await?
}
#[cfg(feature = "mysql")]
Pool::MySql(p) => {
use sqlx::mysql::MySqlQueryAs;
sqlx::query_as(sql).bind(id).fetch_optional(p).await?
}
};
let filite: Option<Filite> = match row {
Some(row) => Some(row.try_into()?),
None => None,
};
Ok(filite)
}
pub fn fetch_all<'a>(pool: &'a Pool) -> impl Stream<Item = Result<Filite, Error>> + 'a {
let sql = "SELECT * FROM data";
let rows: Pin<Box<dyn Stream<Item = Result<FiliteRow, sqlx::Error>>>> = match pool {
#[cfg(feature = "sqlite")]
Pool::Sqlite(p) => {
use sqlx::sqlite::SqliteQueryAs;
sqlx::query_as(sql).fetch(p)
}
#[cfg(feature = "postgres")]
Pool::Postgres(p) => {
use sqlx::postgres::PgQueryAs;
sqlx::query_as(sql).fetch(p)
}
#[cfg(feature = "mysql")]
Pool::MySql(p) => {
use sqlx::mysql::MySqlQueryAs;
sqlx::query_as(sql).fetch(p)
}
};
rows.map(|r| match r {
Ok(r) => r.try_into(),
Err(e) => Err(e.into()),
})
}
pub async fn fetch_user(username: &str, pool: &Pool) -> Result<Option<User>, Error> {
let sql = "SELECT * FROM users WHERE username = ?";
let row: Option<UserRow> = match pool {
#[cfg(feature = "sqlite")]
Pool::Sqlite(p) => {
use sqlx::sqlite::SqliteQueryAs;
sqlx::query_as(sql).bind(username).fetch_optional(p).await?
}
#[cfg(feature = "postgres")]
Pool::Postgres(p) => {
use sqlx::postgres::PgQueryAs;
sqlx::query_as(sql).bind(username).fetch_optional(p).await?
}
#[cfg(feature = "mysql")]
Pool::MySql(p) => {
use sqlx::mysql::MySqlQueryAs;
sqlx::query_as(sql).bind(username).fetch_optional(p).await?
}
};
let user: Option<User> = match row {
Some(row) => Some(row.try_into()?),
None => None,
};
Ok(user)
}
pub fn fetch_all_users<'a>(pool: &'a Pool) -> impl Stream<Item = Result<User, Error>> + 'a {
let sql = "SELECT * FROM users";
let rows: Pin<Box<dyn Stream<Item = Result<UserRow, sqlx::Error>>>> = match pool {
#[cfg(feature = "sqlite")]
Pool::Sqlite(p) => {
use sqlx::sqlite::SqliteQueryAs;
sqlx::query_as(sql).fetch(p)
}
#[cfg(feature = "postgres")]
Pool::Postgres(p) => {
use sqlx::postgres::PgQueryAs;
sqlx::query_as(sql).fetch(p)
}
#[cfg(feature = "mysql")]
Pool::MySql(p) => {
use sqlx::mysql::MySqlQueryAs;
sqlx::query_as(sql).fetch(p)
}
};
rows.map(|r| match r {
Ok(r) => r.try_into(),
Err(e) => Err(e.into()),
})
}

152
src/db/models.rs Normal file
View File

@ -0,0 +1,152 @@
use anyhow::{anyhow, Error};
// use chrono::{DateTime, Utc};
use std::{
convert::{TryFrom, TryInto},
path::PathBuf,
};
use tokio::task;
#[derive(sqlx::FromRow)]
pub struct FiliteRow {
id: String,
ty: i32,
val: String,
creator: String,
// created: DateTime<Utc>,
visibility: i32,
views: i32,
}
pub enum Filite {
File {
id: String,
path: PathBuf,
creator: String,
// created: DateTime<Utc>,
visibility: Visibility,
#[cfg(feature = "analytics")]
views: i32,
},
Link {
id: String,
url: String,
creator: String,
// created: DateTime<Utc>,
visibility: Visibility,
#[cfg(feature = "analytics")]
views: i32,
},
Text {
id: String,
contents: String,
creator: String,
// created: DateTime<Utc>,
visibility: Visibility,
#[cfg(feature = "analytics")]
views: i32,
},
}
pub enum Visibility {
Public,
Internal,
Private,
}
impl TryFrom<FiliteRow> for Filite {
type Error = Error;
fn try_from(row: FiliteRow) -> Result<Self, Self::Error> {
match row.ty {
0 => Ok(Filite::File {
id: row.id,
path: PathBuf::from(row.val),
creator: row.creator,
// created: row.created,
visibility: row.visibility.try_into()?,
#[cfg(feature = "analytics")]
views: row.views,
}),
1 => Ok(Filite::Link {
id: row.id,
url: row.val,
creator: row.creator,
// created: row.created,
visibility: row.visibility.try_into()?,
#[cfg(feature = "analytics")]
views: row.views,
}),
2 => Ok(Filite::Text {
id: row.id,
contents: row.val,
creator: row.creator,
// created: row.created,
visibility: row.visibility.try_into()?,
#[cfg(feature = "analytics")]
views: row.views,
}),
ty => Err(anyhow!("unknown type {}", ty)),
}
}
}
impl TryFrom<i32> for Visibility {
type Error = Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Visibility::Public),
1 => Ok(Visibility::Internal),
2 => Ok(Visibility::Private),
_ => Err(anyhow!("unknown visibility {}", value)),
}
}
}
#[derive(sqlx::FromRow)]
pub struct UserRow {
username: String,
password: String,
role: i32,
// registered: DateTime<Utc>,
}
pub struct User {
pub username: String,
pub password: String,
pub role: Role,
// pub registered: DateTime<Utc>,
}
pub enum Role {
User,
Admin,
}
impl TryFrom<UserRow> for User {
type Error = Error;
fn try_from(value: UserRow) -> Result<Self, Self::Error> {
Ok(User {
username: value.username,
password: value.password,
role: value.role.try_into()?,
})
}
}
impl TryFrom<i32> for Role {
type Error = Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Role::User),
1 => Ok(Role::Admin),
_ => Err(anyhow!("unknown role {}", value)),
}
}
}
impl User {
pub async fn verify_password(&self, password: &str) -> Result<bool, Error> {
let encoded = self.password.clone();
let pwd = password.as_bytes().to_vec();
Ok(task::spawn_blocking(move || argon2::verify_encoded(&encoded, &pwd)).await??)
}
}

View File

@ -63,17 +63,3 @@ impl Pool {
builder
}
}
#[macro_export]
macro_rules! pool {
($pool:expr) => {{
match $pool {
#[cfg(feature = "sqlite")]
$crate::db::Pool::Sqlite(p) => p,
#[cfg(feature = "postgres")]
$crate::db::Pool::Postgres(p) => p,
#[cfg(feature = "mysql")]
$crate::db::Pool::Mysql(p) => p,
}
}};
}