Rewrite to rust

This commit is contained in:
2023-07-23 21:36:47 +02:00
parent 47b280c205
commit 4ec26ce4c0
49 changed files with 15881 additions and 2521 deletions

View File

@@ -0,0 +1,61 @@
use axum::{Router, response::IntoResponse, routing::{get, post}, extract::Path, Json, http::StatusCode};
use chrono::Duration;
use crate::{prisma::chat_donate_notifications, db::get_prisma_client};
async fn is_need_send(
Path(chat_id): Path<i64>
) -> impl IntoResponse {
const NOTIFICATION_DELTA_DAYS: i64 = 60;
let client = get_prisma_client().await;
let notification = client.chat_donate_notifications()
.find_unique(chat_donate_notifications::chat_id::equals(chat_id))
.exec()
.await
.unwrap();
match notification {
Some(notification) => {
let now = chrono::offset::Local::now().naive_local();
let check_date = now - Duration::days(NOTIFICATION_DELTA_DAYS);
let result = notification.sended.naive_local() < check_date;
Json(result).into_response()
},
None => Json(true).into_response(),
}
}
async fn mark_sended(
Path(chat_id): Path<i64>
) -> impl IntoResponse {
let client = get_prisma_client().await;
let _ = client.chat_donate_notifications()
.upsert(
chat_donate_notifications::chat_id::equals(chat_id),
chat_donate_notifications::create(
chat_id,
chrono::offset::Local::now().into(),
vec![]
),
vec![
chat_donate_notifications::sended::set(
chrono::offset::Local::now().into()
)
]
);
StatusCode::OK
}
pub fn get_router() -> Router {
Router::new()
.route("/:chat_id/is_need_send", get(is_need_send))
.route("/:chat_id", post(mark_sended))
}

66
src/views/languages.rs Normal file
View File

@@ -0,0 +1,66 @@
use axum::{Router, response::IntoResponse, routing::get, Json, extract::Path, http::StatusCode};
use serde::Serialize;
use crate::{prisma::language, db::get_prisma_client};
#[derive(Serialize)]
pub struct LanguageDetail {
pub id: i32,
pub label: String,
pub code: String,
}
impl From<language::Data> for LanguageDetail {
fn from(value: language::Data) -> Self {
let language::Data { id, label, code, .. } = value;
Self {
id,
label,
code
}
}
}
async fn get_languages() -> impl IntoResponse {
let client = get_prisma_client().await;
let languages: Vec<LanguageDetail> = client.language()
.find_many(vec![])
.exec()
.await
.unwrap()
.into_iter()
.map(|item| item.into())
.collect();
Json(languages).into_response()
}
async fn get_language_by_code(
Path(code): Path<String>
) -> impl IntoResponse {
let client = get_prisma_client().await;
let language = client.language()
.find_unique(language::code::equals(code))
.exec()
.await
.unwrap();
match language {
Some(v) => Json::<LanguageDetail>(v.into()).into_response(),
None => StatusCode::NOT_FOUND.into_response(),
}
}
pub fn get_router() -> Router {
Router::new()
.route("/", get(get_languages))
.route("/:code", get(get_language_by_code))
}

36
src/views/mod.rs Normal file
View File

@@ -0,0 +1,36 @@
use axum::{Router, response::Response, http::{StatusCode, self, Request}, middleware::{Next, self}};
use crate::config::CONFIG;
pub mod users;
pub mod pagination;
pub mod languages;
pub mod donate_notifications;
async fn auth<B>(req: Request<B>, next: Next<B>) -> Result<Response, StatusCode> {
let auth_header = req.headers()
.get(http::header::AUTHORIZATION)
.and_then(|header| header.to_str().ok());
let auth_header = if let Some(auth_header) = auth_header {
auth_header
} else {
return Err(StatusCode::UNAUTHORIZED);
};
if auth_header != CONFIG.api_key {
return Err(StatusCode::UNAUTHORIZED);
}
Ok(next.run(req).await)
}
pub fn get_router() -> Router {
Router::new()
.nest("/users/", users::get_router())
.nest("/languages/", languages::get_router())
.nest("/donate_notifications/", donate_notifications::get_router())
.layer(middleware::from_fn(auth))
}

49
src/views/pagination.rs Normal file
View File

@@ -0,0 +1,49 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
pub struct Pagination {
#[serde(default = "default_page")]
pub page: usize,
#[serde(default = "default_size")]
pub size: usize
}
fn default_page() -> usize { 1 }
fn default_size() -> usize { 50 }
impl Pagination {
pub fn skip(&self) -> i64 {
((self.page - 1) * self.size).try_into().unwrap()
}
pub fn take(&self) -> i64 {
self.size.try_into().unwrap()
}
}
#[derive(Serialize)]
pub struct Page<T> where T: Serialize {
pub items: Vec<T>,
pub total: usize,
pub page: usize,
pub size: usize,
pub pages: usize
}
impl<T> Page<T> where T: Serialize {
pub fn create(
items: Vec<T>,
items_count: i64,
pagination: Pagination
) -> Self {
Self {
items,
total: items_count.try_into().unwrap(),
page: pagination.page,
size: pagination.size,
pages: (items_count as f64 / pagination.size as f64).ceil() as usize
}
}
}

170
src/views/users/mod.rs Normal file
View File

@@ -0,0 +1,170 @@
pub mod serializers;
pub mod utils;
use axum::{Router, response::IntoResponse, routing::{get, post}, extract::{Query, Path, self}, Json, http::StatusCode};
use crate::{prisma::{user_settings, language_to_user, user_activity}, db::get_prisma_client};
use self::{serializers::{UserDetail, CreateOrUpdateUserData}, utils::update_languages};
use super::pagination::{Pagination, Page};
async fn get_users(
pagination: Query<Pagination>
) -> impl IntoResponse {
let pagination: Pagination = pagination.0;
let client = get_prisma_client().await;
let users_count = client.user_settings()
.count(vec![])
.exec()
.await
.unwrap();
let users: Vec<UserDetail> = client.user_settings()
.find_many(vec![])
.with(
user_settings::languages::fetch(vec![])
.with(
language_to_user::language::fetch()
)
)
.order_by(user_settings::id::order(prisma_client_rust::Direction::Asc))
.skip(pagination.skip())
.take(pagination.take())
.exec()
.await
.unwrap()
.into_iter()
.map(|item| item.into())
.collect();
Json(Page::create(
users,
users_count,
pagination
)).into_response()
}
async fn get_user(
Path(user_id): Path<i64>
) -> impl IntoResponse {
let client = get_prisma_client().await;
let user = client.user_settings()
.find_unique(user_settings::user_id::equals(user_id))
.with(
user_settings::languages::fetch(vec![])
.with(
language_to_user::language::fetch()
)
)
.exec()
.await
.unwrap();
if user.is_none() {
return StatusCode::NOT_FOUND.into_response();
}
Json::<UserDetail>(user.unwrap().into()).into_response()
}
async fn create_or_update_user(
extract::Json(data): extract::Json<CreateOrUpdateUserData>
) -> impl IntoResponse {
let client = get_prisma_client().await;
let user = client.user_settings()
.upsert(
user_settings::user_id::equals(data.user_id),
user_settings::create(
data.user_id,
data.last_name.clone(),
data.first_name.clone(),
data.username.clone(),
data.source.clone(),
vec![]
),
vec![
user_settings::last_name::set(data.last_name),
user_settings::first_name::set(data.first_name),
user_settings::username::set(data.username),
user_settings::source::set(data.source)
]
)
.with(
user_settings::languages::fetch(vec![])
.with(
language_to_user::language::fetch()
)
)
.exec()
.await
.unwrap();
let user_id = user.id;
update_languages(user, data.allowed_langs).await;
let user = client.user_settings()
.find_unique(user_settings::id::equals(user_id))
.with(
user_settings::languages::fetch(vec![])
.with(
language_to_user::language::fetch()
)
)
.exec()
.await
.unwrap()
.unwrap();
Json::<UserDetail>(user.into()).into_response()
}
async fn update_activity(
Path(user_id): Path<i64>,
) -> impl IntoResponse {
let client = get_prisma_client().await;
let user = client.user_settings()
.find_unique(user_settings::user_id::equals(user_id))
.exec()
.await
.unwrap();
let user = match user {
Some(v) => v,
None => return StatusCode::NOT_FOUND.into_response(),
};
let _ = client.user_activity()
.upsert(
user_activity::user_id::equals(user.id),
user_activity::create(
chrono::offset::Local::now().into(),
user_settings::id::equals(user.id),
vec![]
),
vec![
user_activity::updated::set(chrono::offset::Local::now().into())
]
)
.exec()
.await;
StatusCode::OK.into_response()
}
pub fn get_router() -> Router {
Router::new()
.route("/", get(get_users))
.route("/:user_id", get(get_user))
.route("/", post(create_or_update_user))
.route("/:user_id/update_activity", post(update_activity))
}

View File

@@ -0,0 +1,66 @@
use serde::{Serialize, Deserialize};
use crate::prisma::{user_settings, language};
#[derive(Serialize)]
pub struct UserLanguage {
pub id: i32,
pub label: String,
pub code: String,
}
impl From<language::Data> for UserLanguage {
fn from(value: language::Data) -> Self {
Self {
id: value.id,
label: value.label,
code: value.code
}
}
}
#[derive(Serialize)]
pub struct UserDetail {
pub id: i32,
pub user_id: i64,
pub last_name: String,
pub first_name: String,
pub username: String,
pub source: String,
pub allowed_langs: Vec<UserLanguage>
}
impl From<user_settings::Data> for UserDetail {
fn from(value: user_settings::Data) -> Self {
let allowed_langs: Vec<UserLanguage> = value
.languages.unwrap()
.into_iter()
.map(|item| *item.language.unwrap())
.map(|item| item.into())
.collect();
Self {
id: value.id,
user_id: value.user_id,
last_name: value.last_name,
first_name: value.first_name,
username: value.username,
source: value.source,
allowed_langs
}
}
}
#[derive(Deserialize)]
pub struct CreateOrUpdateUserData {
pub user_id: i64,
pub last_name: String,
pub first_name: String,
pub username: String,
pub source: String,
pub allowed_langs: Vec<String>
}

68
src/views/users/utils.rs Normal file
View File

@@ -0,0 +1,68 @@
use std::collections::HashMap;
use crate::{prisma::{user_settings, language, language_to_user}, db::get_prisma_client};
pub async fn update_languages(
user: user_settings::Data,
new_langs: Vec<String>
) {
let client = get_prisma_client().await;
// Delete
{
let need_delete: Vec<_> = user.languages().unwrap()
.iter()
.map(|item| {
let language::Data{ id, code, .. } = *item.clone().language.unwrap();
(id, code)
})
.filter(|(_, code)| !new_langs.contains(code))
.map(|(id, _)| id)
.collect();
let _ = client.language_to_user()
.delete_many(
vec![language_to_user::id::in_vec(need_delete)]
)
.exec()
.await;
}
// Create
{
let languages: HashMap<_, _> = client.language()
.find_many(vec![])
.exec()
.await
.unwrap()
.into_iter()
.map(|l| (l.code, l.id))
.collect();
let current_langs: Vec<_> = user.languages().unwrap()
.iter()
.map(|item| item.clone().language.unwrap().code)
.collect();
let need_create: Vec<i32> = new_langs
.into_iter()
.filter(|code| !current_langs.contains(code))
.map(|code| *languages.get(&code).unwrap())
.collect();
let _ = client.language_to_user()
.create_many(
need_create
.iter()
.map(|language_id| language_to_user::create_unchecked(
*language_id,
user.id,
vec![]
))
.collect()
)
.exec()
.await;
}
}