@@ -0,0 +1,100 @@ | |||
use std::collections::HashMap; | |||
use serde::{Deserialize, Serialize}; | |||
use actix_web::{web, HttpRequest, HttpResponse, Responder}; | |||
use chrono::NaiveDateTime; | |||
use crate::models::models::Comment; | |||
use crate::repositories::comment::{create_comment, get_issue_comments}; | |||
use crate::repositories::session::get_username_from_session; | |||
#[derive(Deserialize)] | |||
pub struct AddCommentRequest { | |||
pub content: String, | |||
pub parent: Option<i64>, | |||
pub issue_id: i32, | |||
} | |||
#[derive(Serialize)] | |||
pub struct AddCommentResponse { | |||
pub content: String, | |||
pub parent: Option<i64>, | |||
pub telegram_handle: String, | |||
pub created_at: NaiveDateTime, | |||
} | |||
pub async fn add_comment(req: HttpRequest, data: web::Json<AddCommentRequest>) -> impl Responder { | |||
if let Some(session) = req.cookie("session") { | |||
if let Some(username) = get_username_from_session(session.value().to_string()) { | |||
let new_comment = create_comment(data.content.clone(), data.parent, username.clone(), data.issue_id); | |||
HttpResponse::Ok().json( | |||
AddCommentResponse { | |||
content: new_comment.content, | |||
parent: new_comment.parent, | |||
telegram_handle: username.clone(), | |||
created_at: new_comment.created_at.unwrap() | |||
}) | |||
} else { | |||
HttpResponse::Unauthorized().into() | |||
} | |||
} else { | |||
HttpResponse::Unauthorized().into() | |||
} | |||
} | |||
#[derive(Deserialize)] | |||
pub struct GetCommentsRequest { | |||
pub issue_id: i32, | |||
} | |||
#[derive(Serialize, Clone)] | |||
pub struct CommentWithChildren { | |||
pub comment: Comment, | |||
pub children: Vec<CommentWithChildren>, | |||
} | |||
#[derive(Serialize)] | |||
pub struct GetCommentsResponse { | |||
pub comments: Vec<CommentWithChildren>, | |||
} | |||
fn build_comment_tree(comments: Vec<Comment>) -> Vec<CommentWithChildren> { | |||
let mut comment_map: HashMap<i64, CommentWithChildren> = HashMap::new(); | |||
let mut child_map: HashMap<i64, Vec<i64>> = HashMap::new(); | |||
let mut root_ids: Vec<i64> = Vec::new(); | |||
for comment in comments { | |||
let id = comment.id; | |||
let comment_with_children = CommentWithChildren { | |||
comment: comment.clone(), | |||
children: Vec::new(), | |||
}; | |||
comment_map.insert(id, comment_with_children); | |||
if let Some(parent_id) = comment.parent { | |||
child_map.entry(parent_id).or_default().push(id); | |||
} else { | |||
root_ids.push(id); | |||
} | |||
} | |||
fn build_tree(id: i64, comment_map: &mut HashMap<i64, CommentWithChildren>, child_map: &HashMap<i64, Vec<i64>>) -> CommentWithChildren { | |||
let mut comment = comment_map.remove(&id).unwrap(); | |||
if let Some(child_ids) = child_map.get(&id) { | |||
comment.children = child_ids.iter() | |||
.map(|&child_id| build_tree(child_id, comment_map, child_map)) | |||
.collect(); | |||
} | |||
comment | |||
} | |||
root_ids.into_iter() | |||
.map(|id| build_tree(id, &mut comment_map, &child_map)) | |||
.collect() | |||
} | |||
pub async fn get_comments(data: web::Query<GetCommentsRequest>) -> impl Responder { | |||
let comments = build_comment_tree(get_issue_comments(data.issue_id)); | |||
HttpResponse::Ok().json(GetCommentsResponse { | |||
comments | |||
}) | |||
} |
@@ -0,0 +1,153 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use actix_web::{web, HttpRequest, HttpResponse, Responder}; | |||
use crate::models::models::IssueWithSummaryAndVotes; | |||
use crate::repositories; | |||
use crate::repositories::issue::{get_issue_vote_equity, get_vote_for_user, record_vote_for_issue, FilterOptions}; | |||
use crate::repositories::session::get_user_id; | |||
#[derive(Serialize, Deserialize, Clone)] | |||
enum IssueVote { | |||
Positive, | |||
Negative, | |||
} | |||
#[derive(Deserialize)] | |||
pub struct CreateIssueRequest { | |||
title: String, | |||
paragraphs: Vec<String>, | |||
} | |||
#[derive(Serialize)] | |||
pub struct CreateIssueResponse { | |||
title: String, | |||
paragraphs: Vec<String>, | |||
} | |||
pub async fn add_issue(req: HttpRequest, data: web::Json<CreateIssueRequest>) -> impl Responder { | |||
if let Some(session_id) = req.cookie("session") { | |||
if let Some(session) = repositories::session::get_session(session_id.value().to_string()) { | |||
repositories::issue::create_issue(&data.title, &data.paragraphs, &session.username); | |||
let resp = CreateIssueResponse { | |||
title: String::from(&data.title), | |||
paragraphs: data.paragraphs.clone(), | |||
}; | |||
return HttpResponse::Ok().json(resp); | |||
} | |||
HttpResponse::Unauthorized().into() | |||
} else { | |||
HttpResponse::Unauthorized().into() | |||
} | |||
} | |||
#[derive(Deserialize)] | |||
pub struct IssuesPaginationRequest { | |||
min_positive_votes: Option<i64>, | |||
min_votes: Option<i64>, | |||
offset: i32, | |||
limit: i16, | |||
} | |||
#[derive(Serialize)] | |||
pub struct IssuesPaginationResponse { | |||
issues: Vec<IssueWithSummaryAndVotes>, | |||
} | |||
pub async fn list_issues(req: web::Query<IssuesPaginationRequest>) -> impl Responder { | |||
let issues = repositories::issue::get_issues( | |||
Option::from(FilterOptions { | |||
min_positive_votes: req.min_positive_votes, | |||
min_votes: req.min_votes, | |||
}), | |||
req.offset, | |||
req.limit | |||
); | |||
HttpResponse::Ok().json(IssuesPaginationResponse { issues }) | |||
} | |||
#[derive(Deserialize)] | |||
pub struct GetParagraphsRequest { | |||
issue_id: i32, | |||
} | |||
#[derive(Serialize)] | |||
pub struct GetParagraphsResponse { | |||
paragraphs: Vec<Option<String>>, | |||
} | |||
pub async fn get_paragraphs(query: web::Query<GetParagraphsRequest>) -> impl Responder { | |||
let paragraphs = repositories::issue::get_paragraphs(query.issue_id); | |||
HttpResponse::Ok().json(GetParagraphsResponse { paragraphs }) | |||
} | |||
#[derive(Deserialize, Clone)] | |||
pub struct VoteIssueRequest { | |||
issue_id: i32, | |||
vote: IssueVote, | |||
} | |||
#[derive(Serialize)] | |||
pub struct VoteIssueResponse { | |||
issue_id: i32, | |||
equity: i64, | |||
positive: Option<bool>, | |||
} | |||
pub async fn vote_issue(req: HttpRequest, data: web::Json<VoteIssueRequest>) -> impl Responder { | |||
let positive_vote = match data.vote { | |||
IssueVote::Positive => true, | |||
IssueVote::Negative => false, | |||
}; | |||
if let Some(session_id) = req.cookie("session") { | |||
if let Some(user_id) = get_user_id(session_id.value().to_string()) { | |||
record_vote_for_issue(positive_vote, data.issue_id, user_id); | |||
let resp = VoteIssueResponse { | |||
issue_id: data.issue_id, | |||
equity: get_issue_vote_equity(data.issue_id), | |||
positive: match data.vote { | |||
IssueVote::Positive => Some(true), | |||
IssueVote::Negative => Some(false), | |||
}, | |||
}; | |||
HttpResponse::Ok().json(resp) | |||
} else { | |||
HttpResponse::Unauthorized().into() | |||
} | |||
} else { | |||
HttpResponse::Unauthorized().into() | |||
} | |||
} | |||
#[derive(Deserialize)] | |||
pub struct GetUserVoteRequest { | |||
issue_id: i32, | |||
} | |||
#[derive(Serialize)] | |||
pub struct GetUserVoteResponse { | |||
vote: Option<IssueVote>, | |||
} | |||
pub async fn get_user_vote(req: HttpRequest, query: web::Query<GetUserVoteRequest>) -> impl Responder { | |||
let mut resp = GetUserVoteResponse { vote: None }; | |||
if let Some(session_cookie) = req.cookie("session") { | |||
return if let Some(user_id) = get_user_id(session_cookie.value().to_string()) { | |||
let vote = get_vote_for_user(query.issue_id, user_id); | |||
resp = GetUserVoteResponse { | |||
vote: match vote { | |||
Some(val) => { | |||
if val { | |||
Some(IssueVote::Positive) | |||
} else { | |||
Some(IssueVote::Negative) | |||
} | |||
}, | |||
None => None, | |||
} | |||
}; | |||
HttpResponse::Ok().json(resp) | |||
} else { | |||
HttpResponse::Ok().json(GetUserVoteResponse { vote: None }) | |||
} | |||
} | |||
HttpResponse::Ok().json(resp) | |||
} |
@@ -0,0 +1,3 @@ | |||
pub(crate) mod issue; | |||
pub(crate) mod session; | |||
pub(crate) mod comment; |
@@ -0,0 +1,45 @@ | |||
use actix_web::{web, HttpRequest, HttpResponse, Responder}; | |||
use serde::{Deserialize, Serialize}; | |||
use crate::repositories; | |||
use crate::repositories::session::get_username_from_session; | |||
#[derive(Deserialize, Clone)] | |||
pub struct AuthenticateRequest { | |||
user_id: i64, | |||
auth_date: i64, | |||
username: String, | |||
first_name: String, | |||
last_name: String, | |||
photo_url: String, | |||
} | |||
#[derive(Serialize)] | |||
pub struct AuthenticateResponse { | |||
session_id: String, | |||
} | |||
pub async fn authenticate(req: web::Json<AuthenticateRequest>) -> impl Responder { | |||
let session_id = repositories::session::authenticate_session( | |||
req.clone().user_id, | |||
req.clone().auth_date, | |||
req.clone().username, | |||
req.clone().first_name, | |||
req.clone().last_name, | |||
req.clone().photo_url | |||
); | |||
let resp = AuthenticateResponse { session_id }; | |||
HttpResponse::Ok().json(resp) | |||
} | |||
#[derive(Serialize)] | |||
pub struct GetUsernameResponse { | |||
username: Option<String>, | |||
} | |||
pub async fn get_username(req: HttpRequest) -> impl Responder { | |||
if let Some(session_cookie) = req.cookie("session") { | |||
let username = get_username_from_session(session_cookie.value().to_string()); | |||
return HttpResponse::Ok().json(GetUsernameResponse { username }) | |||
} | |||
HttpResponse::Unauthorized().into() | |||
} |
@@ -0,0 +1,12 @@ | |||
use std::env; | |||
use diesel::Connection; | |||
use diesel::PgConnection; | |||
use dotenvy::dotenv; | |||
pub fn establish_connection() -> PgConnection { | |||
dotenv().ok(); | |||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); | |||
PgConnection::establish(&database_url) | |||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) | |||
} |
@@ -0,0 +1 @@ | |||
pub(crate) mod db; |
@@ -0,0 +1 @@ | |||
pub mod models; |
@@ -0,0 +1,149 @@ | |||
use diesel::sql_types::Integer; | |||
use diesel::sql_types::Nullable; | |||
use diesel::sql_types::SmallInt; | |||
use diesel::sql_types::Timestamp; | |||
use diesel::sql_types::Text; | |||
use diesel::sql_types::BigInt; | |||
use chrono::NaiveDateTime; | |||
use diesel::{Insertable, Queryable, QueryableByName, Selectable}; | |||
use serde::Serialize; | |||
#[derive(Queryable, Selectable, Serialize, Clone)] | |||
#[diesel(table_name = crate::schema::issues)] | |||
#[diesel(check_for_backend(diesel::pg::Pg))] | |||
pub struct Issue { | |||
pub id: i32, | |||
pub title: String, | |||
pub paragraph_count: Option<i16>, | |||
pub telegram_handle: String, | |||
pub created_at: Option<NaiveDateTime> | |||
} | |||
#[derive(QueryableByName, Serialize, Clone)] | |||
pub struct IssueWithSummaryAndVotes { | |||
#[diesel(sql_type = Integer)] | |||
pub id: i32, | |||
#[diesel(sql_type = Text)] | |||
pub title: String, | |||
#[diesel(sql_type = Text)] | |||
pub summary: String, | |||
#[diesel(sql_type = Nullable<SmallInt>)] | |||
pub paragraph_count: Option<i16>, | |||
#[diesel(sql_type = Text)] | |||
pub telegram_handle: String, | |||
#[diesel(sql_type = Nullable<Timestamp>)] | |||
pub created_at: Option<NaiveDateTime>, | |||
#[diesel(sql_type = BigInt)] | |||
pub total_votes: i64, | |||
#[diesel(sql_type = BigInt)] | |||
pub positive_votes: i64, | |||
} | |||
#[derive(Insertable)] | |||
#[diesel(table_name = crate::schema::issues)] | |||
pub struct NewIssue { | |||
pub title: String, | |||
pub paragraph_count: i16, | |||
pub telegram_handle: String, | |||
} | |||
#[derive(Queryable, Selectable)] | |||
#[diesel(table_name = crate::schema::sessions)] | |||
#[diesel(check_for_backend(diesel::pg::Pg))] | |||
pub struct Session { | |||
pub id: i64, | |||
pub session_id: String, | |||
pub auth_date: Option<i64>, | |||
pub username: Option<String>, | |||
pub first_name: Option<String>, | |||
pub last_name: Option<String>, | |||
pub photo_url: Option<String> | |||
} | |||
#[derive(Insertable)] | |||
#[diesel(table_name = crate::schema::sessions)] | |||
pub struct NewSession { | |||
pub user_id: Option<i64>, | |||
pub session_id: String, | |||
pub auth_date: i64, | |||
pub username: Option<String>, | |||
pub first_name: Option<String>, | |||
pub last_name: Option<String>, | |||
pub photo_url: Option<String> | |||
} | |||
#[derive(Queryable, Selectable, Serialize, Clone)] | |||
#[diesel(table_name = crate::schema::paragraphs)] | |||
#[diesel(check_for_backend(diesel::pg::Pg))] | |||
pub struct Paragraph { | |||
pub id: i64, | |||
pub content: String, | |||
pub index: i32, | |||
pub post_id: i32, | |||
} | |||
#[derive(Insertable)] | |||
#[diesel(table_name = crate::schema::paragraphs)] | |||
pub struct NewParagraph { | |||
pub content: Option<String>, | |||
pub index: i32, | |||
pub post_id: i32, | |||
} | |||
#[derive(Queryable, Selectable, Serialize, Clone)] | |||
#[diesel(table_name = crate::schema::issue_votes)] | |||
#[diesel(check_for_backend(diesel::pg::Pg))] | |||
pub struct IssueVote { | |||
pub id: i64, | |||
pub positive: Option<bool>, | |||
pub issue_id: i32, | |||
pub user_id: i64, | |||
} | |||
#[derive(Insertable)] | |||
#[diesel(table_name = crate::schema::issue_votes)] | |||
pub struct NewIssueVote { | |||
pub positive: bool, | |||
pub issue_id: i32, | |||
pub user_id: i64, | |||
} | |||
#[derive(Queryable, Selectable, Serialize, Clone)] | |||
#[diesel(table_name = crate::schema::comments)] | |||
#[diesel(check_for_backend(diesel::pg::Pg))] | |||
pub struct Comment { | |||
pub id: i64, | |||
pub content: String, | |||
pub parent: Option<i64>, | |||
pub telegram_handle: String, | |||
pub issue_id: i32, | |||
pub created_at: Option<NaiveDateTime> | |||
} | |||
#[derive(Insertable)] | |||
#[diesel(table_name = crate::schema::comments)] | |||
pub struct NewComment { | |||
pub content: String, | |||
pub parent: Option<i64>, | |||
pub telegram_handle: String, | |||
pub issue_id: i32, | |||
pub created_at: NaiveDateTime | |||
} | |||
#[derive(Queryable, Selectable, Serialize, Clone)] | |||
#[diesel(table_name = crate::schema::comment_votes)] | |||
#[diesel(check_for_backend(diesel::pg::Pg))] | |||
pub struct CommentVote { | |||
pub id: i64, | |||
pub positive: Option<bool>, | |||
pub comment_id: i64, | |||
pub user_id: i64, | |||
} | |||
#[derive(Insertable)] | |||
#[diesel(table_name = crate::schema::comment_votes)] | |||
pub struct NewCommentVote { | |||
pub positive: Option<bool>, | |||
pub comment_id: Option<i64>, | |||
pub user_id: Option<i64>, | |||
} |
@@ -0,0 +1,38 @@ | |||
use crate::db::db::establish_connection; | |||
use crate::models::models::{Comment, NewComment}; | |||
use crate::schema::comments; | |||
use chrono::Utc; | |||
use diesel::{alias, ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl, SelectableHelper, NullableExpressionMethods}; | |||
use crate::schema; | |||
pub fn get_issue_comments(issue_id: i32) -> Vec<Comment> { | |||
let connection = &mut establish_connection(); | |||
let (c1, c2) = alias!(comments as c1, comments as c2); | |||
comments::table | |||
.left_join(c1.on(comments::parent.eq(comments::id.nullable()))) | |||
.filter(comments::issue_id.eq(issue_id)) | |||
.select(Comment::as_select()) | |||
.load::<Comment>(connection) | |||
.unwrap_or_else(|_| Vec::new()) | |||
} | |||
pub fn create_comment(content: String, parent: Option<i64>, telegram_handle: String, issue_id: i32) -> Comment { | |||
let connection = &mut establish_connection(); | |||
let new_comment = NewComment { | |||
content, | |||
parent, | |||
telegram_handle, | |||
issue_id, | |||
created_at: Utc::now().naive_utc() | |||
}; | |||
let insertion = diesel::insert_into(comments::table) | |||
.values(&new_comment) | |||
.returning(Comment::as_select()) | |||
.get_result(connection) | |||
.expect("Error saving comment"); | |||
insertion | |||
} |
@@ -0,0 +1,161 @@ | |||
use crate::db::db::establish_connection; | |||
use crate::models::models::{Issue, IssueWithSummaryAndVotes, NewIssue, NewIssueVote, NewParagraph, Paragraph}; | |||
use crate::schema::issue_votes::{issue_id, positive}; | |||
use crate::schema::paragraphs; | |||
use crate::schema::paragraphs::{content, index}; | |||
use crate::schema::{issue_votes, issues}; | |||
use chrono::NaiveDateTime; | |||
use diesel; | |||
use diesel::associations::HasTable; | |||
use diesel::dsl::count; | |||
use diesel::result::DatabaseErrorKind; | |||
use diesel::result::Error::DatabaseError; | |||
use diesel::{sql_query, BoolExpressionMethods, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; | |||
use serde::Deserialize; | |||
#[derive(Deserialize)] | |||
pub struct FilterOptions { | |||
pub min_positive_votes: Option<i64>, | |||
pub min_votes: Option<i64>, | |||
} | |||
pub fn get_issues(filters: Option<FilterOptions>, offset: i32, limit: i16) -> Vec<IssueWithSummaryAndVotes> { | |||
let connection = &mut establish_connection(); | |||
let query = sql_query("select i.id, title, content as summary, i.paragraph_count, telegram_handle, i.created_at, | |||
count(v.id) as total_votes, | |||
sum(case when v.positive then 1 else 0 end) as positive_votes | |||
from issues i | |||
inner join paragraphs p on i.id = p.post_id | |||
inner join issue_votes v on i.id = v.issue_id | |||
where p.index = 0 | |||
group by v.issue_id, title, telegram_handle, content, i.id;"); | |||
let mut issues_with_content: Vec<IssueWithSummaryAndVotes> = query | |||
.load::<IssueWithSummaryAndVotes>(connection) | |||
.unwrap_or_default() | |||
.into_iter() | |||
.collect(); | |||
if let Some(filter_info) = filters { | |||
if let Some(positive_votes) = filter_info.min_positive_votes { | |||
issues_with_content = issues_with_content | |||
.iter() | |||
.filter(|i| i.positive_votes >= positive_votes) | |||
.map(|i| i.clone()) | |||
.collect() | |||
} | |||
if let Some(min_votes) = filter_info.min_votes { | |||
issues_with_content = issues_with_content | |||
.iter() | |||
.filter(|i| i.total_votes >= min_votes) | |||
.map(|i| i.clone()) | |||
.collect() | |||
} | |||
} | |||
let len = issues_with_content.len(); | |||
let i1 = offset.max(0) as usize; | |||
let i2 = i1.saturating_add(limit as usize).min(len); | |||
issues_with_content[i1..i2].to_vec() | |||
} | |||
pub fn create_issue(title: &String, paragraphs: &Vec<String>, telegram_handle: &Option<String>) -> Option<Issue> { | |||
let connection = &mut establish_connection(); | |||
if let Some(tg_handle) = telegram_handle { | |||
let new_issue = NewIssue { | |||
title: title.to_string(), | |||
paragraph_count: paragraphs.len() as i16, | |||
telegram_handle: tg_handle.to_string(), | |||
}; | |||
let insertion = diesel::insert_into(issues::table) | |||
.values(&new_issue) | |||
.returning(Issue::as_select()) | |||
.get_result(connection) | |||
.expect("Error saving new issue"); | |||
let _ = diesel::insert_into(paragraphs::table) | |||
.values(paragraphs.iter().enumerate().map(|(i, p)| | |||
NewParagraph { | |||
content: Some(p.clone()), | |||
index: i as i32, | |||
post_id: insertion.id | |||
}).collect::<Vec<NewParagraph>>()) | |||
.returning(Paragraph::as_select()) | |||
.get_results(connection) | |||
.expect("Error saving paragraphs"); | |||
return Some(insertion) | |||
} | |||
None | |||
} | |||
pub fn get_paragraphs(other_issue_id: i32) -> Vec<Option<String>> { | |||
let connection = &mut establish_connection(); | |||
paragraphs::table | |||
.filter(paragraphs::post_id.eq(other_issue_id)) | |||
.order(index) | |||
.select(content) | |||
.load::<String>(connection) | |||
.unwrap_or_default() | |||
.into_iter() | |||
.map(Some) | |||
.collect::<Vec<Option<String>>>() | |||
} | |||
pub fn record_vote_for_issue(other_positive: bool, other_issue_id: i32, user_id: i64) -> bool { | |||
let connection = &mut establish_connection(); | |||
let result = diesel::insert_into(issue_votes::table) | |||
.values(NewIssueVote { positive: other_positive, issue_id: other_issue_id, user_id }) | |||
.execute(connection); | |||
match result { | |||
Ok(_) => true, | |||
Err(DatabaseError(kind, _)) => { | |||
if let DatabaseErrorKind::UniqueViolation = kind { | |||
let _ = diesel::update( | |||
issue_votes::table | |||
.filter( | |||
issue_votes::user_id.eq(user_id).and(issue_votes::issue_id.eq(issue_id)) | |||
) | |||
) | |||
.set(issue_votes::positive.eq(positive)) | |||
.execute(connection); | |||
return true | |||
} | |||
false | |||
}, | |||
_ => false | |||
} | |||
} | |||
pub fn get_issue_vote_equity(issue_identifier: i32) -> i64 { | |||
let connection = &mut establish_connection(); | |||
let positive_votes: i64 = issue_votes::table | |||
.filter(issue_votes::issue_id.eq(issue_identifier).and(issue_votes::positive.eq(true))) | |||
.count() | |||
.get_result(connection) | |||
.unwrap(); | |||
let negative_votes: i64 = issue_votes::table | |||
.filter(issue_votes::issue_id.eq(issue_identifier).and(issue_votes::positive.eq(false))) | |||
.count() | |||
.get_result(connection) | |||
.unwrap(); | |||
positive_votes - negative_votes | |||
} | |||
pub fn get_vote_for_user(other_issue_id: i32, other_user_id: i64) -> Option<bool> { | |||
let connection = &mut establish_connection(); | |||
let result: Option<bool> = issue_votes::table | |||
.filter(issue_votes::issue_id.eq(other_issue_id).and(issue_votes::user_id.eq(other_user_id))) | |||
.limit(1) | |||
.select(issue_votes::positive) | |||
.get_result::<Option<bool>>(connection) | |||
.unwrap_or(None); | |||
result | |||
} |
@@ -0,0 +1,3 @@ | |||
pub(crate) mod issue; | |||
pub(crate) mod session; | |||
pub(crate) mod comment; |
@@ -0,0 +1,66 @@ | |||
use diesel::{OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper}; | |||
use diesel::ExpressionMethods; | |||
use uuid::Uuid; | |||
use crate::db::db::establish_connection; | |||
use crate::models::models::{NewSession, Session}; | |||
use crate::schema::sessions; | |||
use crate::schema::sessions::dsl::sessions as sessions_dsl; | |||
use crate::schema::sessions::session_id; | |||
pub fn get_session(sess_id: String) -> Option<Session> { | |||
let connection = &mut establish_connection(); | |||
sessions_dsl | |||
.filter(session_id.eq(sess_id)) | |||
.select(Session::as_select()) | |||
.first(connection) | |||
.optional() | |||
.expect("Error loading session") | |||
} | |||
pub fn authenticate_session(user_id: i64, auth_date: i64, username: String, first_name: String, last_name: String, photo_url: String) -> String { | |||
let sess_id: Uuid = Uuid::new_v4(); | |||
let connection = &mut establish_connection(); | |||
let new_session = NewSession { | |||
user_id: Some(user_id), | |||
session_id: sess_id.clone().to_string(), | |||
auth_date, | |||
username: Some(username), | |||
first_name: Some(first_name), | |||
last_name: Some(last_name), | |||
photo_url: Some(photo_url), | |||
}; | |||
let result = diesel::insert_into(sessions::table) | |||
.values(&new_session) | |||
.returning(Session::as_select()) | |||
.get_result(connection) | |||
.unwrap(); | |||
result.session_id | |||
} | |||
pub fn get_user_id(sess_id: String) -> Option<i64> { | |||
let connection = &mut establish_connection(); | |||
let user_id: Option<i64> = sessions::table | |||
.filter(session_id.eq(sess_id)) | |||
.limit(1) | |||
.select(sessions::user_id) | |||
.get_result(connection) | |||
.optional() | |||
.unwrap_or(None); | |||
user_id | |||
} | |||
pub fn get_username_from_session(sess_id: String) -> Option<String> { | |||
let connection = &mut establish_connection(); | |||
let username = sessions::table | |||
.filter(sessions::session_id.eq(sess_id)) | |||
.limit(1) | |||
.select(sessions::username) | |||
.get_result(connection) | |||
.unwrap_or_default(); | |||
username | |||
} |