first commit
This commit is contained in:
commit
2eb8aac027
91 files changed
+20139
No files matched your search
@@ -0,0 +1,108 @@
|
||||
use crate::{
|
||||
db::{Table, row_i64},
|
||||
error::AppResult,
|
||||
passwords, routes,
|
||||
state::AppState,
|
||||
};
|
||||
use axum::{Json, extract::State};
|
||||
use axum_extra::extract::cookie::CookieJar;
|
||||
use serde::Serialize;
|
||||
use std::sync::Arc;
|
||||
#[derive(Serialize)]
|
||||
pub struct Health {
|
||||
status: &'static str,
|
||||
version: &'static str,
|
||||
}
|
||||
pub async fn health(State(state): State<Arc<AppState>>) -> AppResult<Json<Health>> {
|
||||
state.db.ping().await?;
|
||||
Ok(Json(Health {
|
||||
status: "ok",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
}))
|
||||
}
|
||||
pub async fn overview(
|
||||
State(state): State<Arc<AppState>>,
|
||||
jar: CookieJar,
|
||||
) -> AppResult<Json<serde_json::Value>> {
|
||||
let s = routes::session(&state, &jar)?;
|
||||
let domains = count(&state, Table::Domain, "domain<>'ALL'").await?;
|
||||
let mailboxes = count_scoped(&state, Table::Mailbox, "domain", &s).await?;
|
||||
let aliases = count_scoped(&state, Table::Alias, "domain", &s).await?;
|
||||
Ok(Json(
|
||||
serde_json::json!({"domains":if s.role.is_global(){domains}else{s.allowed_domains.iter().filter(|d|d.as_str()!="ALL").count() as i64},"mailboxes":mailboxes,"aliases":aliases,"role":s.role,"username":s.username,"features":&state.config.features}),
|
||||
))
|
||||
}
|
||||
pub async fn schema(
|
||||
State(state): State<Arc<AppState>>,
|
||||
jar: CookieJar,
|
||||
) -> AppResult<Json<serde_json::Value>> {
|
||||
let s = routes::session(&state, &jar)?;
|
||||
s.require_global()?;
|
||||
let warnings = state.db.check_schema(&state.config).await?;
|
||||
Ok(Json(
|
||||
serde_json::json!({"ok":warnings.is_empty(),"warnings":warnings,"database_backend":state.config.database.backend}),
|
||||
))
|
||||
}
|
||||
pub async fn features(
|
||||
State(state): State<Arc<AppState>>,
|
||||
jar: CookieJar,
|
||||
) -> AppResult<Json<serde_json::Value>> {
|
||||
let _ = routes::session(&state, &jar)?;
|
||||
Ok(Json(
|
||||
serde_json::json!({"features":&state.config.features,"defaults":{"page_size":state.config.defaults.page_size,"quota_multiplier":state.config.defaults.quota_multiplier,"quota_level_med_pct":state.config.defaults.quota_level_med_pct,"quota_level_high_pct":state.config.defaults.quota_level_high_pct,"motd_user":state.config.defaults.motd_user,"motd_admin":state.config.defaults.motd_admin,"motd_superadmin":state.config.defaults.motd_superadmin},"password":{"configured":state.config.password.scheme,"supported":passwords::supported_schemes()},"development_mock":state.config.database.dev_mock,"database_read_only":state.config.database.read_only,"dkim":{"default_backend":state.config.dkim.default_backend,"user_read_only":state.config.dkim.user_read_only},"rspamd_dkim":{"enabled":state.config.rspamd_dkim.enabled,"default_selector":state.config.rspamd_dkim.default_selector,"development_mock":state.config.rspamd_dkim.dev_mock},"sieve":{"enabled":state.config.sieve.enabled,"port":state.config.sieve.port,"tls_mode":state.config.sieve.tls_mode,"read_only":state.config.sieve.read_only},"version":env!("CARGO_PKG_VERSION")}),
|
||||
))
|
||||
}
|
||||
async fn count(state: &AppState, table: Table, where_sql: &str) -> AppResult<i64> {
|
||||
let t = state.db.table(table);
|
||||
let r = sqlx::query(&format!("SELECT COUNT(*) AS n FROM {t} WHERE {where_sql}"))
|
||||
.fetch_one(state.db.pool())
|
||||
.await?;
|
||||
Ok(row_i64(&r, "n"))
|
||||
}
|
||||
async fn count_scoped(
|
||||
state: &AppState,
|
||||
table: Table,
|
||||
col: &str,
|
||||
s: &crate::auth::Session,
|
||||
) -> AppResult<i64> {
|
||||
let t = state.db.table(table);
|
||||
if s.role.is_global() {
|
||||
let r = sqlx::query(&format!("SELECT COUNT(*) AS n FROM {t}"))
|
||||
.fetch_one(state.db.pool())
|
||||
.await?;
|
||||
return Ok(row_i64(&r, "n"));
|
||||
}
|
||||
if !s.role.is_admin() {
|
||||
let q = state.db.q(&format!(
|
||||
"SELECT COUNT(*) AS n FROM {t} WHERE {}=?",
|
||||
if matches!(table, Table::Mailbox) {
|
||||
"username"
|
||||
} else {
|
||||
"address"
|
||||
}
|
||||
));
|
||||
let r = sqlx::query(&q)
|
||||
.bind(&s.username)
|
||||
.fetch_one(state.db.pool())
|
||||
.await?;
|
||||
return Ok(row_i64(&r, "n"));
|
||||
}
|
||||
let ds: Vec<_> = s
|
||||
.allowed_domains
|
||||
.iter()
|
||||
.filter(|d| d.as_str() != "ALL")
|
||||
.collect();
|
||||
if ds.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
let q = state.db.q(&format!(
|
||||
"SELECT COUNT(*) AS n FROM {t} WHERE {col} IN ({})",
|
||||
vec!["?"; ds.len()].join(",")
|
||||
));
|
||||
let mut x = sqlx::query(&q);
|
||||
for d in ds {
|
||||
x = x.bind(d.as_str());
|
||||
}
|
||||
let r = x.fetch_one(state.db.pool()).await?;
|
||||
Ok(row_i64(&r, "n"))
|
||||
}
|
||||
Reference in new issue
Block a user