//
// Copyright (c) 2025 rustmailer.com (https://rustmailer.com)
//
// This file is part of the Bichon Email Archiving Project
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
use crate::modules::common::auth::ClientContext;
use crate::modules::dashboard::DashboardStats;
use crate::modules::error::code::ErrorCode;
use crate::modules::rest::api::ApiTags;
use crate::modules::rest::ApiResult;
use crate::modules::settings::cli::SETTINGS;
use crate::modules::settings::proxy::Proxy;
use crate::modules::settings::SystemConfigurations;
use crate::modules::users::permissions::Permission;
use crate::modules::version::{fetch_notifications, Notifications};
use crate::raise_error;
use poem_openapi::param::Path;
use poem_openapi::payload::{Json, PlainText};
use poem_openapi::OpenApi;
pub struct SystemApi;
#[OpenApi(prefix_path = "/api/v1", tag = "ApiTags::System")]
impl SystemApi {
/// Retrieves important system notifications for the RustMail service.
///
/// This endpoint returns a consolidated view of all critical system notifications including:
/// - Available version updates
/// - License expiration warnings
#[oai(
method = "get",
path = "/notifications",
operation_id = "get_notifications"
)]
async fn get_notifications(&self) -> ApiResult> {
let notification = fetch_notifications()
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
Ok(Json(notification))
}
/// Get overall dashboard statistics.
///
/// Returns various aggregated metrics about the mail system, such as
/// total email count, total storage size, index usage, top senders,
/// recent activity histogram, and more.
#[oai(
method = "get",
path = "/dashboard-stats",
operation_id = "get_dashboard_stats"
)]
async fn get_dashboard_stats(&self, context: ClientContext) -> ApiResult> {
let stats = DashboardStats::get(context).await?;
Ok(Json(stats))
}
/// Get the full list of SOCKS5 proxy configurations.
#[oai(method = "get", path = "/list-proxy", operation_id = "list_proxy")]
async fn list_proxy(&self, context: ClientContext) -> ApiResult>> {
context
.require_any_permission(vec![
(None, Permission::ACCOUNT_CREATE),
(None, Permission::ROOT),
])
.await?;
let proxies = Proxy::list_all()
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
Ok(Json(proxies))
}
/// Delete a specific proxy configuration by ID. Requires root permission.
#[oai(path = "/proxy/:id", method = "delete", operation_id = "remove_proxy")]
async fn remove_proxy(
&self,
/// The ID of the proxy configuration to delete.
id: Path,
context: ClientContext,
) -> ApiResult<()> {
context
.require_permission(None, Permission::ROOT)
.await?;
Ok(Proxy::delete(id.0).await?)
}
/// Retrieve a specific proxy configuration by ID. Requires root permission.
#[oai(path = "/proxy/:id", method = "get", operation_id = "get_proxy")]
async fn get_proxy(
&self,
/// The ID of the proxy configuration to retrieve.
id: Path,
context: ClientContext,
) -> ApiResult> {
context
.require_permission(None, Permission::ROOT)
.await?;
Ok(Json(Proxy::get(id.0).await?))
}
/// Create a new proxy configuration. Requires root permission.
#[oai(path = "/proxy", method = "post", operation_id = "create_proxy")]
async fn create_proxy(&self, url: PlainText, context: ClientContext) -> ApiResult<()> {
context
.require_permission(None, Permission::ROOT)
.await?;
let entity = Proxy::new(url.0);
Ok(entity.save().await?)
}
/// Update the URL of a specific proxy by ID. Requires root permission.
#[oai(path = "/proxy/:id", method = "post", operation_id = "update_proxy")]
async fn update_proxy(
&self,
id: Path,
url: PlainText,
context: ClientContext,
) -> ApiResult<()> {
context
.require_permission(None, Permission::ROOT)
.await?;
Ok(Proxy::update(id.0, url.0).await?)
}
/// Get system configurations.
///
/// Returns a read-only snapshot of the server configuration
/// resolved at startup. Sensitive values are not exposed.
#[oai(
method = "get",
path = "/system-configurations",
operation_id = "get_system_configurations"
)]
async fn get_system_configurations(
&self,
context: ClientContext,
) -> ApiResult> {
context
.require_permission(None, Permission::ROOT)
.await?;
let config: SystemConfigurations = SystemConfigurations::from(&*SETTINGS);
Ok(Json(config))
}
}