mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-10-26 02:09:07 +00:00
Added telegram
This commit is contained in:
@@ -5,6 +5,7 @@ pub mod templates;
|
||||
pub mod client_configs;
|
||||
pub mod dns_providers;
|
||||
pub mod tasks;
|
||||
pub mod telegram;
|
||||
|
||||
pub use users::*;
|
||||
pub use servers::*;
|
||||
@@ -12,4 +13,5 @@ pub use certificates::*;
|
||||
pub use templates::*;
|
||||
pub use client_configs::*;
|
||||
pub use dns_providers::*;
|
||||
pub use tasks::*;
|
||||
pub use tasks::*;
|
||||
pub use telegram::*;
|
||||
@@ -504,6 +504,7 @@ pub async fn add_user_to_inbound(
|
||||
name: user_name.clone(),
|
||||
comment: user_data["comment"].as_str().map(|s| s.to_string()),
|
||||
telegram_id: user_data["telegram_id"].as_i64(),
|
||||
is_telegram_admin: false,
|
||||
};
|
||||
|
||||
match user_repo.create(create_user_dto).await {
|
||||
|
||||
304
src/web/handlers/telegram.rs
Normal file
304
src/web/handlers/telegram.rs
Normal file
@@ -0,0 +1,304 @@
|
||||
use axum::{
|
||||
extract::{State, Path, Json},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::web::AppState;
|
||||
use crate::database::repository::{UserRepository, TelegramConfigRepository};
|
||||
use crate::database::entities::telegram_config::{CreateTelegramConfigDto, UpdateTelegramConfigDto};
|
||||
|
||||
/// Response for Telegram config
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct TelegramConfigResponse {
|
||||
pub id: Uuid,
|
||||
pub is_active: bool,
|
||||
pub bot_info: Option<BotInfo>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct BotInfo {
|
||||
pub username: String,
|
||||
pub first_name: String,
|
||||
}
|
||||
|
||||
/// Get current Telegram configuration
|
||||
pub async fn get_telegram_config(
|
||||
State(state): State<AppState>,
|
||||
) -> impl IntoResponse {
|
||||
let repo = TelegramConfigRepository::new(state.db.connection());
|
||||
|
||||
match repo.get_latest().await {
|
||||
Ok(Some(config)) => {
|
||||
let mut response = TelegramConfigResponse {
|
||||
id: config.id,
|
||||
is_active: config.is_active,
|
||||
bot_info: None,
|
||||
created_at: config.created_at.to_rfc3339(),
|
||||
updated_at: config.updated_at.to_rfc3339(),
|
||||
};
|
||||
|
||||
// Get bot info if active
|
||||
if config.is_active {
|
||||
if let Ok(status) = get_bot_status(&state).await {
|
||||
response.bot_info = status.bot_info;
|
||||
}
|
||||
}
|
||||
|
||||
Json(response).into_response()
|
||||
}
|
||||
Ok(None) => {
|
||||
StatusCode::NOT_FOUND.into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to get telegram config: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new Telegram configuration
|
||||
pub async fn create_telegram_config(
|
||||
State(state): State<AppState>,
|
||||
Json(dto): Json<CreateTelegramConfigDto>,
|
||||
) -> impl IntoResponse {
|
||||
let repo = TelegramConfigRepository::new(state.db.connection());
|
||||
|
||||
match repo.create(dto).await {
|
||||
Ok(config) => {
|
||||
// Initialize telegram service with new config if active
|
||||
if config.is_active {
|
||||
if let Some(telegram_service) = &state.telegram_service {
|
||||
let _ = telegram_service.update_config(config.id).await;
|
||||
}
|
||||
}
|
||||
|
||||
(StatusCode::CREATED, Json(config)).into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to create telegram config: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update Telegram configuration
|
||||
pub async fn update_telegram_config(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(dto): Json<UpdateTelegramConfigDto>,
|
||||
) -> impl IntoResponse {
|
||||
let repo = TelegramConfigRepository::new(state.db.connection());
|
||||
|
||||
match repo.update(id, dto).await {
|
||||
Ok(Some(config)) => {
|
||||
// Update telegram service
|
||||
if let Some(telegram_service) = &state.telegram_service {
|
||||
let _ = telegram_service.update_config(config.id).await;
|
||||
}
|
||||
|
||||
Json(config).into_response()
|
||||
}
|
||||
Ok(None) => {
|
||||
StatusCode::NOT_FOUND.into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to update telegram config: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete Telegram configuration
|
||||
pub async fn delete_telegram_config(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> impl IntoResponse {
|
||||
let repo = TelegramConfigRepository::new(state.db.connection());
|
||||
|
||||
// Stop bot if this config is active
|
||||
if let Ok(Some(config)) = repo.find_by_id(id).await {
|
||||
if config.is_active {
|
||||
if let Some(telegram_service) = &state.telegram_service {
|
||||
let _ = telegram_service.stop().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match repo.delete(id).await {
|
||||
Ok(true) => StatusCode::NO_CONTENT.into_response(),
|
||||
Ok(false) => StatusCode::NOT_FOUND.into_response(),
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to delete telegram config: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get Telegram bot status
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct BotStatusResponse {
|
||||
pub is_running: bool,
|
||||
pub bot_info: Option<BotInfo>,
|
||||
}
|
||||
|
||||
async fn get_bot_status(state: &AppState) -> Result<BotStatusResponse, String> {
|
||||
if let Some(telegram_service) = &state.telegram_service {
|
||||
let status = telegram_service.get_status().await;
|
||||
|
||||
let bot_info = if status.is_running {
|
||||
// In production, you would get this from the bot API
|
||||
Some(BotInfo {
|
||||
username: "bot".to_string(),
|
||||
first_name: "Bot".to_string(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(BotStatusResponse {
|
||||
is_running: status.is_running,
|
||||
bot_info,
|
||||
})
|
||||
} else {
|
||||
Ok(BotStatusResponse {
|
||||
is_running: false,
|
||||
bot_info: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_telegram_status(
|
||||
State(state): State<AppState>,
|
||||
) -> impl IntoResponse {
|
||||
match get_bot_status(&state).await {
|
||||
Ok(status) => Json(status).into_response(),
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to get bot status: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get list of Telegram admins
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct TelegramAdmin {
|
||||
pub user_id: Uuid,
|
||||
pub name: String,
|
||||
pub telegram_id: Option<i64>,
|
||||
}
|
||||
|
||||
pub async fn get_telegram_admins(
|
||||
State(state): State<AppState>,
|
||||
) -> impl IntoResponse {
|
||||
let repo = UserRepository::new(state.db.connection());
|
||||
|
||||
match repo.get_telegram_admins().await {
|
||||
Ok(admins) => {
|
||||
let response: Vec<TelegramAdmin> = admins
|
||||
.into_iter()
|
||||
.map(|u| TelegramAdmin {
|
||||
user_id: u.id,
|
||||
name: u.name,
|
||||
telegram_id: u.telegram_id,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Json(response).into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to get telegram admins: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add Telegram admin
|
||||
pub async fn add_telegram_admin(
|
||||
State(state): State<AppState>,
|
||||
Path(user_id): Path<Uuid>,
|
||||
) -> impl IntoResponse {
|
||||
let repo = UserRepository::new(state.db.connection());
|
||||
|
||||
match repo.set_telegram_admin(user_id, true).await {
|
||||
Ok(Some(user)) => {
|
||||
// Notify via Telegram if bot is running
|
||||
if let Some(telegram_service) = &state.telegram_service {
|
||||
if let Some(telegram_id) = user.telegram_id {
|
||||
let _ = telegram_service.send_message(
|
||||
telegram_id,
|
||||
"✅ You have been granted admin privileges!".to_string()
|
||||
).await;
|
||||
}
|
||||
}
|
||||
|
||||
Json(user).into_response()
|
||||
}
|
||||
Ok(None) => {
|
||||
StatusCode::NOT_FOUND.into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to add telegram admin: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove Telegram admin
|
||||
pub async fn remove_telegram_admin(
|
||||
State(state): State<AppState>,
|
||||
Path(user_id): Path<Uuid>,
|
||||
) -> impl IntoResponse {
|
||||
let repo = UserRepository::new(state.db.connection());
|
||||
|
||||
match repo.set_telegram_admin(user_id, false).await {
|
||||
Ok(Some(user)) => {
|
||||
// Notify via Telegram if bot is running
|
||||
if let Some(telegram_service) = &state.telegram_service {
|
||||
if let Some(telegram_id) = user.telegram_id {
|
||||
let _ = telegram_service.send_message(
|
||||
telegram_id,
|
||||
"❌ Your admin privileges have been revoked.".to_string()
|
||||
).await;
|
||||
}
|
||||
}
|
||||
|
||||
Json(user).into_response()
|
||||
}
|
||||
Ok(None) => {
|
||||
StatusCode::NOT_FOUND.into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to remove telegram admin: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send test message
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SendMessageRequest {
|
||||
pub chat_id: i64,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
pub async fn send_test_message(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<SendMessageRequest>,
|
||||
) -> impl IntoResponse {
|
||||
if let Some(telegram_service) = &state.telegram_service {
|
||||
match telegram_service.send_message(req.chat_id, req.text).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to send test message: {}", e);
|
||||
(StatusCode::BAD_REQUEST, e.to_string()).into_response()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
StatusCode::SERVICE_UNAVAILABLE.into_response()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user