feat: implement service users

This commit is contained in:
2025-10-28 06:51:34 +04:00
parent b635750e28
commit d1ef5c032e
20 changed files with 450 additions and 126 deletions

1
src/routes/admin/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod service_users;

View File

@@ -0,0 +1,75 @@
use self::schema::*;
use crate::{utility, AppState};
use actix_web::{post, web};
use database::entity::{ActiveServiceUser, UserType};
use database::query::Query;
use database::sea_orm::{ActiveModelTrait, Set};
use objectid::ObjectId;
use web::Json;
#[utoipa::path(responses(
(status = OK, body = Response),
))]
#[post("/create")]
pub async fn create(data_json: Json<Request>, app_state: web::Data<AppState>) -> ServiceResponse {
let service_user =
match Query::find_service_user_by_id(app_state.get_database(), &data_json.name)
.await
.expect("Failed to find service user by name")
{
Some(_) => return Err(ErrorCode::AlreadyExists).into(),
None => {
let new_user = ActiveServiceUser {
id: Set(ObjectId::new().unwrap().to_string()),
name: Set(data_json.name.clone()),
};
new_user
.insert(app_state.get_database())
.await
.expect("Failed to insert service user")
}
};
let access_token = utility::jwt::encode(UserType::Service, &service_user.id);
Ok(Response::new(access_token)).into()
}
mod schema {
use actix_macros::{ErrResponse, OkResponse};
use derive_more::Display;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
#[derive(Debug, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
#[schema(as = ServiceUser::Create::Request)]
pub struct Request {
/// Service username.
pub name: String,
}
#[derive(Serialize, ToSchema, OkResponse)]
#[serde(rename_all = "camelCase")]
#[schema(as = ServiceUser::Create::Response)]
pub struct Response {
access_token: String,
}
impl Response {
pub fn new(access_token: String) -> Self {
Self { access_token }
}
}
pub type ServiceResponse = crate::routes::schema::Response<Response, ErrorCode>;
#[derive(Clone, ToSchema, Display, ErrResponse, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[status_code = "actix_web::http::StatusCode::UNAUTHORIZED"]
#[schema(as = ServiceUser::Create::ErrorCode)]
pub enum ErrorCode {
#[display("Service user with that name already exists.")]
AlreadyExists,
}
}

View File

@@ -0,0 +1,3 @@
mod create;
pub use create::*;

View File

@@ -7,6 +7,7 @@ use crate::{utility, AppState};
use actix_web::{post, web};
use database::query::Query;
use web::Json;
use database::entity::UserType;
async fn sign_in_combined(
data: SignInData,
@@ -40,7 +41,7 @@ async fn sign_in_combined(
}
}
let access_token = utility::jwt::encode(&user.id);
let access_token = utility::jwt::encode(UserType::Default, &user.id);
Ok(UserResponse::from_user_with_token(user, access_token))
}
@@ -184,9 +185,7 @@ mod tests {
let active_user = ActiveUser {
id: Set(id.clone()),
username: Set(username),
password: Set(Some(
bcrypt::hash("example", bcrypt::DEFAULT_COST).unwrap(),
)),
password: Set(Some(bcrypt::hash("example", bcrypt::DEFAULT_COST).unwrap())),
vk_id: Set(None),
telegram_id: Set(None),
group: Set(Some("ИС-214/23".to_string())),

View File

@@ -5,7 +5,7 @@ use crate::routes::schema::ResponseError;
use crate::{utility, AppState};
use actix_web::{post, web};
use database::entity::sea_orm_active_enums::UserRole;
use database::entity::ActiveUser;
use database::entity::{ActiveUser, UserType};
use database::query::Query;
use database::sea_orm::ActiveModelTrait;
use web::Json;
@@ -51,7 +51,7 @@ async fn sign_up_combined(
let active_user: ActiveUser = data.into();
let user = active_user.insert(db).await.unwrap();
let access_token = utility::jwt::encode(&user.id);
let access_token = utility::jwt::encode(UserType::Default, &user.id);
Ok(UserResponse::from_user_with_token(user, access_token))
}

View File

@@ -5,7 +5,7 @@ use crate::{utility, AppState};
use actix_web::{post, web};
use chrono::{DateTime, Duration, Utc};
use database::entity::sea_orm_active_enums::UserRole;
use database::entity::ActiveUser;
use database::entity::{ActiveUser, UserType};
use database::query::Query;
use database::sea_orm::{ActiveModelTrait, Set};
use objectid::ObjectId;
@@ -73,7 +73,7 @@ pub async fn telegram_auth(
}
};
let access_token = utility::jwt::encode(&user.id);
let access_token = utility::jwt::encode(UserType::Default, &user.id);
Ok(Response::new(&access_token, user.group.is_some())).into()
}

View File

@@ -58,10 +58,7 @@ pub async fn telegram_complete(
active_user.group = Set(Some(data.group));
active_user
.update(db)
.await
.expect("Failed to update user");
active_user.update(db).await.expect("Failed to update user");
Ok(()).into()
}

View File

@@ -1,3 +1,4 @@
pub mod admin;
pub mod auth;
pub mod flow;
pub mod schedule;

View File

@@ -0,0 +1,65 @@
use self::schema::*;
use crate::routes::schedule::schema::ScheduleEntryResponse;
use crate::routes::schema::ResponseError;
use crate::AppState;
use actix_web::{get, web};
#[utoipa::path(responses(
(status = OK, body = ScheduleEntryResponse),
(
status = SERVICE_UNAVAILABLE,
body = ResponseError<ErrorCode>,
example = json!({
"code": "NO_SCHEDULE",
"message": "Schedule not parsed yet."
})
),
(
status = NOT_FOUND,
body = ResponseError<ErrorCode>,
example = json!({
"code": "NOT_FOUND",
"message": "Required group not found."
})
),
))]
#[get("/group/{group_name}")]
pub async fn group_by_name(
path: web::Path<String>,
app_state: web::Data<AppState>,
) -> ServiceResponse {
let group_name = path.into_inner();
match app_state
.get_schedule_snapshot("eng_polytechnic")
.await
.unwrap()
.data
.groups
.get(&group_name)
{
None => Err(ErrorCode::NotFound),
Some(entry) => Ok(entry.clone().into()),
}
.into()
}
mod schema {
use crate::routes::schedule::schema::ScheduleEntryResponse;
use actix_macros::ErrResponse;
use derive_more::Display;
use serde::Serialize;
use utoipa::ToSchema;
pub type ServiceResponse = crate::routes::schema::Response<ScheduleEntryResponse, ErrorCode>;
#[derive(Clone, Serialize, Display, ToSchema, ErrResponse)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[schema(as = GroupByNameSchedule::ErrorCode)]
pub enum ErrorCode {
/// Group not found.
#[status_code = "actix_web::http::StatusCode::NOT_FOUND"]
#[display("Required group not found.")]
NotFound,
}
}

View File

@@ -1,5 +1,6 @@
mod cache_status;
mod group;
mod group_by_name;
mod group_names;
mod get;
mod schema;
@@ -8,6 +9,7 @@ mod teacher_names;
pub use cache_status::*;
pub use group::*;
pub use group_by_name::*;
pub use group_names::*;
pub use get::*;
pub use teacher::*;