added more db stuff, NOT WORKING
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user