added more db stuff, NOT WORKING
This commit is contained in:
		
							parent
							
								
									c35de0642e
								
							
						
					
					
						commit
						4bd3425b1b
					
				
							
								
								
									
										146
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										146
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -311,6 +311,21 @@ version = "0.2.16"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "android-tzdata"
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "android_system_properties"
 | 
			
		||||
version = "0.1.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anstream"
 | 
			
		||||
version = "0.6.13"
 | 
			
		||||
| 
						 | 
				
			
			@ -551,6 +566,12 @@ dependencies = [
 | 
			
		|||
 "alloc-stdlib",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bumpalo"
 | 
			
		||||
version = "3.15.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "byteorder"
 | 
			
		||||
version = "1.5.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -594,6 +615,21 @@ version = "1.0.0"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "chrono"
 | 
			
		||||
version = "0.4.37"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "android-tzdata",
 | 
			
		||||
 "iana-time-zone",
 | 
			
		||||
 "js-sys",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "wasm-bindgen",
 | 
			
		||||
 "windows-targets 0.52.4",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cipher"
 | 
			
		||||
version = "0.4.4"
 | 
			
		||||
| 
						 | 
				
			
			@ -673,6 +709,12 @@ dependencies = [
 | 
			
		|||
 "version_check",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "core-foundation-sys"
 | 
			
		||||
version = "0.8.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cpufeatures"
 | 
			
		||||
version = "0.2.12"
 | 
			
		||||
| 
						 | 
				
			
			@ -1193,6 +1235,29 @@ version = "2.1.0"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "iana-time-zone"
 | 
			
		||||
version = "0.1.60"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "android_system_properties",
 | 
			
		||||
 "core-foundation-sys",
 | 
			
		||||
 "iana-time-zone-haiku",
 | 
			
		||||
 "js-sys",
 | 
			
		||||
 "wasm-bindgen",
 | 
			
		||||
 "windows-core",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "iana-time-zone-haiku"
 | 
			
		||||
version = "0.1.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "idna"
 | 
			
		||||
version = "0.5.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -1252,6 +1317,15 @@ dependencies = [
 | 
			
		|||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "js-sys"
 | 
			
		||||
version = "0.3.69"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "wasm-bindgen",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "language-tags"
 | 
			
		||||
version = "0.3.2"
 | 
			
		||||
| 
						 | 
				
			
			@ -1820,9 +1894,9 @@ dependencies = [
 | 
			
		|||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_json"
 | 
			
		||||
version = "1.0.114"
 | 
			
		||||
version = "1.0.115"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
 | 
			
		||||
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "itoa",
 | 
			
		||||
 "ryu",
 | 
			
		||||
| 
						 | 
				
			
			@ -2512,6 +2586,60 @@ version = "0.1.0"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wasm-bindgen"
 | 
			
		||||
version = "0.2.92"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "wasm-bindgen-macro",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wasm-bindgen-backend"
 | 
			
		||||
version = "0.2.92"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bumpalo",
 | 
			
		||||
 "log",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn 2.0.53",
 | 
			
		||||
 "wasm-bindgen-shared",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wasm-bindgen-macro"
 | 
			
		||||
version = "0.2.92"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "quote",
 | 
			
		||||
 "wasm-bindgen-macro-support",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wasm-bindgen-macro-support"
 | 
			
		||||
version = "0.2.92"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn 2.0.53",
 | 
			
		||||
 "wasm-bindgen-backend",
 | 
			
		||||
 "wasm-bindgen-shared",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wasm-bindgen-shared"
 | 
			
		||||
version = "0.2.92"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "webpki-roots"
 | 
			
		||||
version = "0.25.4"
 | 
			
		||||
| 
						 | 
				
			
			@ -2528,15 +2656,18 @@ dependencies = [
 | 
			
		|||
 "anyhow",
 | 
			
		||||
 "askama",
 | 
			
		||||
 "bcrypt",
 | 
			
		||||
 "bitflags 2.5.0",
 | 
			
		||||
 "camino",
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "clap",
 | 
			
		||||
 "crypto",
 | 
			
		||||
 "env_logger",
 | 
			
		||||
 "futures",
 | 
			
		||||
 "log",
 | 
			
		||||
 "rand",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "sqlx",
 | 
			
		||||
 "time",
 | 
			
		||||
 "toml",
 | 
			
		||||
 "uuid",
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			@ -2551,6 +2682,15 @@ dependencies = [
 | 
			
		|||
 "wasite",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-core"
 | 
			
		||||
version = "0.52.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "windows-targets 0.52.4",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-sys"
 | 
			
		||||
version = "0.48.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,13 +24,19 @@ actix-web = "4.5.1"
 | 
			
		|||
actix-web-lab = "0.20.2"
 | 
			
		||||
askama = "0.12.1"
 | 
			
		||||
 | 
			
		||||
#? web::api
 | 
			
		||||
chrono = { version = "0.4.37", features = ["serde"] }
 | 
			
		||||
 | 
			
		||||
#? database
 | 
			
		||||
sqlx = { version = "0.7.4", features = ["tls-rustls", "runtime-tokio", "postgres", "uuid"] }
 | 
			
		||||
uuid = { version = "1.8.0", features = ["v4"] }
 | 
			
		||||
time = { version = "0.3.34", features = ["macros"] }
 | 
			
		||||
# time = { version = "0.3.34", features = ["macros"] }
 | 
			
		||||
crypto = { version = "0.5.1", features = ["digest", "password-hash"] }
 | 
			
		||||
bcrypt = "0.15.1"
 | 
			
		||||
futures = "0.3.30"
 | 
			
		||||
bitflags = "2.5.0"
 | 
			
		||||
rand = "0.8.5"
 | 
			
		||||
serde_json = "1.0.115"
 | 
			
		||||
 | 
			
		||||
[profile.dev.package.sqlx-macros]
 | 
			
		||||
opt-level = 3
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										0
									
								
								migrations/posts/down.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								migrations/posts/down.sql
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										11
									
								
								migrations/posts/up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								migrations/posts/up.sql
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
 | 
			
		||||
CREATE TABLE IF NOT EXISTS Posts (
 | 
			
		||||
    id UUID NOT NULL UNIQUE,
 | 
			
		||||
    title TEXT NOT NULL,
 | 
			
		||||
    descr TEXT NOT NULL,
 | 
			
		||||
    img_url TEXT NOT NULL,
 | 
			
		||||
    origin_url TEXT NOT NULL,
 | 
			
		||||
    original_request JSON NOT NULL,
 | 
			
		||||
    posted_on TIMESTAMP NOT NULL
 | 
			
		||||
    PRIMARY KEY (id)
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										0
									
								
								migrations/tokens/down.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								migrations/tokens/down.sql
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										7
									
								
								migrations/tokens/up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								migrations/tokens/up.sql
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
 | 
			
		||||
CREATE TABLE IF NOT EXISTS Tokens (
 | 
			
		||||
    token TEXT NOT NULL UNIQUE,
 | 
			
		||||
    owner_id UUID NOT NULL,
 | 
			
		||||
    permissions BIGINT NOT NULL,
 | 
			
		||||
    PRIMARY KEY (token)
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
-- Your SQL goes here
 | 
			
		||||
 | 
			
		||||
CREATE TABLE IF NOT EXISTS Users (
 | 
			
		||||
    id UUID NOT NULL UNIQUE,
 | 
			
		||||
    email TEXT NOT NULL,
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +1,12 @@
 | 
			
		|||
use bitflags::bitflags;
 | 
			
		||||
 | 
			
		||||
pub mod users;
 | 
			
		||||
pub mod tokens;
 | 
			
		||||
pub mod posts;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bitflags! {
 | 
			
		||||
    struct Permissions: i64 {
 | 
			
		||||
        const MAKE_POST = 1 << 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										135
									
								
								src/database/models/posts.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/database/models/posts.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,135 @@
 | 
			
		|||
use serde_json::Value;
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
use sqlx::Row;
 | 
			
		||||
use crate::database::Database;
 | 
			
		||||
use futures::TryStreamExt;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, sqlx::FromRow)]
 | 
			
		||||
pub struct Post {
 | 
			
		||||
    pub id: uuid::Uuid,
 | 
			
		||||
    pub title: String,
 | 
			
		||||
    pub descr: String,
 | 
			
		||||
    pub img_url: String,
 | 
			
		||||
    pub origin_url: String,
 | 
			
		||||
    pub original_request: Value,
 | 
			
		||||
    pub posted_on: i64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
impl Post {
 | 
			
		||||
    pub async fn create_new(db: &mut Database, title: String, descr: String, img_url: String, origin_url: String, orignal_request: Value) -> anyhow::Result<Uuid> {
 | 
			
		||||
        let id = Uuid::new_v4();
 | 
			
		||||
        let posted_on = chrono::Utc::now().timestamp_millis();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        sqlx::query(r#"
 | 
			
		||||
            INSERT INTO posts ( id, title, descr, img_url, origin_url, original_request, posted_on )
 | 
			
		||||
            VALUES ( $1, $2, $3, $4, 0 )
 | 
			
		||||
            RETURNING id
 | 
			
		||||
        "#)
 | 
			
		||||
            .bind(id)
 | 
			
		||||
            .bind(title)
 | 
			
		||||
            .bind(descr)
 | 
			
		||||
            .bind(img_url)
 | 
			
		||||
            .bind(origin_url)
 | 
			
		||||
            .bind(orignal_request)
 | 
			
		||||
            .bind(posted_on)
 | 
			
		||||
            .execute(db.connection())
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        log::debug!("Created post with id '{id}'");
 | 
			
		||||
 | 
			
		||||
        Ok(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_by_id(db: &mut Database, id: Uuid) -> anyhow::Result<Option<Self>>{
 | 
			
		||||
        let mut rows = sqlx::query("SELECT * FROM posts WHERE id = $1")
 | 
			
		||||
            .bind(id)
 | 
			
		||||
            .fetch(db.connection());
 | 
			
		||||
 | 
			
		||||
        while let Some(row) = rows.try_next().await? {
 | 
			
		||||
            return Ok(Some(Self {
 | 
			
		||||
                id: row.try_get("id")?,
 | 
			
		||||
                title: row.try_get("title")?,
 | 
			
		||||
                descr: row.try_get("descr")?,
 | 
			
		||||
                img_url: row.try_get("img_url")?,
 | 
			
		||||
                origin_url: row.try_get("origin_url")?,
 | 
			
		||||
                original_request: row.try_get("original_request")?,
 | 
			
		||||
                posted_on: row.try_get("posted_on")?,
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(None)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_last_n(db: &mut Database, n: usize) -> anyhow::Result<Vec<Post>>{
 | 
			
		||||
        let mut rows = sqlx::query("SELECT * FROM posts")
 | 
			
		||||
            .fetch(db.connection());
 | 
			
		||||
 | 
			
		||||
        let mut posts = Vec::new();
 | 
			
		||||
 | 
			
		||||
        while let Some(row) = rows.try_next().await? {
 | 
			
		||||
            let post = Self {
 | 
			
		||||
                id: row.try_get("id")?,
 | 
			
		||||
                title: row.try_get("title")?,
 | 
			
		||||
                descr: row.try_get("descr")?,
 | 
			
		||||
                img_url: row.try_get("img_url")?,
 | 
			
		||||
                origin_url: row.try_get("origin_url")?,
 | 
			
		||||
                original_request: row.try_get("original_request")?,
 | 
			
		||||
                posted_on: row.try_get("posted_on")?,
 | 
			
		||||
            };
 | 
			
		||||
            posts.push(post);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        posts.sort_unstable_by(|a, b| {
 | 
			
		||||
            b.posted_on.cmp(&a.posted_on)
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let posts = if posts.len() < n {
 | 
			
		||||
            posts.to_vec()
 | 
			
		||||
        } else {
 | 
			
		||||
            posts[..n].to_vec()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        Ok(posts)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn save(&self, db: &mut Database) -> anyhow::Result<()> {
 | 
			
		||||
        let _ = sqlx::query(r#"
 | 
			
		||||
                UPDATE posts
 | 
			
		||||
                SET id = $1
 | 
			
		||||
                SET title = $2
 | 
			
		||||
                SET descr = $3
 | 
			
		||||
                SET img_url = $4
 | 
			
		||||
                SET origin_url = $5
 | 
			
		||||
                SET original_request = $6
 | 
			
		||||
                SET posted_on = $7
 | 
			
		||||
 | 
			
		||||
            "#)
 | 
			
		||||
            .bind(&self.id)
 | 
			
		||||
            .bind(&self.title)
 | 
			
		||||
            .bind(&self.descr)
 | 
			
		||||
            .bind(&self.img_url)
 | 
			
		||||
            .bind(&self.origin_url)
 | 
			
		||||
            .bind(&self.original_request)
 | 
			
		||||
            .bind(&self.posted_on)
 | 
			
		||||
            .execute(db.connection()).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn remove(&self, db: &mut Database) -> anyhow::Result<()> {
 | 
			
		||||
        sqlx::query(r#"
 | 
			
		||||
            DELETE FROM posts
 | 
			
		||||
            WHERE id = $1
 | 
			
		||||
        "#)
 | 
			
		||||
            .bind(self.id)
 | 
			
		||||
            .execute(db.connection())
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        log::debug!("Deleted post with id '{}'", self.id);
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								src/database/models/tokens.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/database/models/tokens.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,118 @@
 | 
			
		|||
use rand::Rng;
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
use sqlx::Row;
 | 
			
		||||
use crate::database::Database;
 | 
			
		||||
use futures::TryStreamExt;
 | 
			
		||||
 | 
			
		||||
use super::Permissions;
 | 
			
		||||
 | 
			
		||||
#[derive(sqlx::FromRow)]
 | 
			
		||||
pub struct Token {
 | 
			
		||||
    pub token: String,
 | 
			
		||||
    pub owner_id: Uuid,
 | 
			
		||||
    pub permissions: Permissions,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
impl Token {
 | 
			
		||||
    pub async fn create_new(db: &mut Database, owner_id: Uuid, permissions: Permissions) -> anyhow::Result<Self> {
 | 
			
		||||
 | 
			
		||||
        let token = generate_token(32);
 | 
			
		||||
 | 
			
		||||
        sqlx::query(r#"
 | 
			
		||||
            INSERT INTO tokens ( token, owner_id, permissions )
 | 
			
		||||
            VALUES ( $1, $2, $3 )
 | 
			
		||||
        "#)
 | 
			
		||||
            .bind(&token)
 | 
			
		||||
            .bind(owner_id)
 | 
			
		||||
            .bind(permissions.bits())
 | 
			
		||||
            .execute(db.connection())
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        log::debug!("Created token '{token}'");
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            token,
 | 
			
		||||
            owner_id,
 | 
			
		||||
            permissions,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_by_token(db: &mut Database, token: String) -> anyhow::Result<Option<Self>>{
 | 
			
		||||
        let mut rows = sqlx::query("SELECT * FROM tokens WHERE token = $1")
 | 
			
		||||
            .bind(token)
 | 
			
		||||
            .fetch(db.connection());
 | 
			
		||||
 | 
			
		||||
        while let Some(row) = rows.try_next().await? {
 | 
			
		||||
            return Ok(Some(Self {
 | 
			
		||||
                token: row.try_get("token")?,
 | 
			
		||||
                owner_id: row.try_get("owner_id")?,
 | 
			
		||||
                permissions: Permissions::from_bits(row.try_get("permissions")?).unwrap(),
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(None)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_by_owner_id(db: &mut Database, owner_id: String) -> anyhow::Result<Option<Self>>{
 | 
			
		||||
        let mut rows = sqlx::query("SELECT * FROM tokens WHERE owner_id = $1")
 | 
			
		||||
            .bind(owner_id)
 | 
			
		||||
            .fetch(db.connection());
 | 
			
		||||
 | 
			
		||||
        while let Some(row) = rows.try_next().await? {
 | 
			
		||||
            return Ok(Some(Self {
 | 
			
		||||
                token: row.try_get("token")?,
 | 
			
		||||
                owner_id: row.try_get("owner_id")?,
 | 
			
		||||
                permissions: Permissions::from_bits(row.try_get("permissions")?).unwrap(),
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(None)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    pub async fn save(&self, db: &mut Database) -> anyhow::Result<()> {
 | 
			
		||||
        let _ = sqlx::query(r#"
 | 
			
		||||
                UPDATE users
 | 
			
		||||
                SET token = $1
 | 
			
		||||
                SET owner_id = $2
 | 
			
		||||
                SET permissions = $5
 | 
			
		||||
            "#)
 | 
			
		||||
            .bind(&self.token)
 | 
			
		||||
            .bind(&self.owner_id)
 | 
			
		||||
            .bind(&self.permissions.bits())
 | 
			
		||||
            .execute(db.connection()).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn remove(&self, db: &mut Database) -> anyhow::Result<()> {
 | 
			
		||||
        sqlx::query(r#"
 | 
			
		||||
            DELETE FROM tokens
 | 
			
		||||
            WHERE token = $1
 | 
			
		||||
        "#)
 | 
			
		||||
            .bind(&self.token)
 | 
			
		||||
            .execute(db.connection())
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        log::debug!("Deleted token '{}'", self.token);
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const TOKEN_CHARSET: &'static [char] = &['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
 | 
			
		||||
 | 
			
		||||
fn generate_token(len: u8) -> String {
 | 
			
		||||
    let mut token = String::new();
 | 
			
		||||
 | 
			
		||||
    for _ in 0..len {
 | 
			
		||||
        let rand = rand::thread_rng().gen_range(0..TOKEN_CHARSET.len());
 | 
			
		||||
        token.push(TOKEN_CHARSET[rand])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    token
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ use crate::database::Database;
 | 
			
		|||
use futures::TryStreamExt;
 | 
			
		||||
 | 
			
		||||
#[derive(sqlx::FromRow)]
 | 
			
		||||
pub struct Users {
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub id: uuid::Uuid,
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ pub struct Users {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
impl Users {
 | 
			
		||||
impl User {
 | 
			
		||||
    pub async fn create_new(db: &mut Database, email: String, username: String, password: String) -> anyhow::Result<Uuid> {
 | 
			
		||||
        let id = Uuid::new_v4();
 | 
			
		||||
        let hash = bcrypt::hash(password, 15)?;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ mod templates;
 | 
			
		|||
 | 
			
		||||
use std::sync::Mutex;
 | 
			
		||||
 | 
			
		||||
use actix_web::{web, App, HttpServer};
 | 
			
		||||
use actix_web::{web, App, HttpServer, Route};
 | 
			
		||||
use actix_files as actix_fs;
 | 
			
		||||
 | 
			
		||||
use crate::{config::definition::Config, database::Database};
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ pub(crate) async fn start_actix(config: &Config, database: Database) -> anyhow::
 | 
			
		|||
        App::new()
 | 
			
		||||
            .app_data(actix_web::web::Data::new(Mutex::new(database.clone())))
 | 
			
		||||
            .route("/", web::get().to(routes::index)) // index.html
 | 
			
		||||
            .service(routes::api::get_scope())
 | 
			
		||||
            .service(actix_fs::Files::new("/static", "./static").index_file("index.html")) // static directory
 | 
			
		||||
            .service(web::redirect("/favicon.ico", "/static/favicon.ico")) //? special redirect for favicon
 | 
			
		||||
    })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								src/web/routes/api/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/web/routes/api/mod.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
mod webhooks;
 | 
			
		||||
 | 
			
		||||
use actix_web::{web, Route, Scope};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub fn get_scope() -> Scope {
 | 
			
		||||
    Scope::new("/api")
 | 
			
		||||
        .service(
 | 
			
		||||
            webhooks::get_scope()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								src/web/routes/api/webhooks/github.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/web/routes/api/webhooks/github.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
use std::{borrow::BorrowMut, sync::Mutex};
 | 
			
		||||
 | 
			
		||||
use actix_web::{http::header, web::{self, Data}, HttpRequest, HttpResponse, HttpResponseBuilder, Responder, Result, Scope};
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
 | 
			
		||||
use crate::database::{models::{self, tokens::Token}, Database};
 | 
			
		||||
 | 
			
		||||
pub fn get_scope() -> Scope {
 | 
			
		||||
    Scope::new("/github")
 | 
			
		||||
        .service(
 | 
			
		||||
            web::resource("/push")
 | 
			
		||||
                .to(handler)
 | 
			
		||||
        )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn handler(req: HttpRequest, body: web::Json<Value>, db: Data<Mutex<Database>>) -> Result<impl Responder> {
 | 
			
		||||
    let Some(auth) = req.headers().get(header::AUTHORIZATION) else {
 | 
			
		||||
        return Ok(HttpResponse::Unauthorized());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let Ok(token) = auth.to_str() else {
 | 
			
		||||
        return Ok(HttpResponse::Unauthorized());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let token = models::tokens::Token::get_by_token(
 | 
			
		||||
        db.lock().unwrap().borrow_mut(),
 | 
			
		||||
    token.to_string()
 | 
			
		||||
        ).await;
 | 
			
		||||
 | 
			
		||||
    let Ok(token) = token else {
 | 
			
		||||
        return Ok(HttpResponse::Unauthorized());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let Some(token) = token else {
 | 
			
		||||
        return Ok(HttpResponse::Unauthorized());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let Some(event_type) = req.headers().get("X-GitHub-Event") else {
 | 
			
		||||
        return Ok(HttpResponse::BadRequest());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let Ok(event_type) = event_type.to_str() else {
 | 
			
		||||
        return Ok(HttpResponse::BadRequest());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    match event_type {
 | 
			
		||||
        "release" => {
 | 
			
		||||
            release_handler(db, token, body).await
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _ => Ok(HttpResponse::Ok())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub async fn release_handler(db: Data<Mutex<Database>>, token: Token, body: web::Json<Value>) -> Result<HttpResponseBuilder> {
 | 
			
		||||
    let Some(release) = body.get("release") else {
 | 
			
		||||
        return Ok(HttpResponse::BadRequest());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let Some(origin_url) = release.get("repository") else {
 | 
			
		||||
        return Ok(HttpResponse::BadRequest());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    models::posts::Post::create_new(
 | 
			
		||||
        db.lock().unwrap().borrow_mut(),
 | 
			
		||||
        title,
 | 
			
		||||
        descr,
 | 
			
		||||
        img_url,
 | 
			
		||||
        origin_url,
 | 
			
		||||
        orignal_request
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/web/routes/api/webhooks/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/web/routes/api/webhooks/mod.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
use actix_web::{web, Scope};
 | 
			
		||||
 | 
			
		||||
mod github;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub fn get_scope() -> Scope {
 | 
			
		||||
    Scope::new("/wh")
 | 
			
		||||
        .service(
 | 
			
		||||
            github::get_scope()
 | 
			
		||||
        )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
pub mod api;
 | 
			
		||||
 | 
			
		||||
use std::sync::Mutex;
 | 
			
		||||
 | 
			
		||||
use actix_web_lab::respond::Html;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user