Files
appks/service/context.rs
T
2026-06-07 11:30:56 +08:00

66 lines
2.0 KiB
Rust

use std::sync::Arc;
use uuid::Uuid;
use crate::cache::AppCache;
use crate::cache::redis::AppRedis;
use crate::config::AppConfig;
use crate::error::AppError;
use crate::etcd::EtcdRegistry;
use crate::models::db::AppDatabase;
use crate::queue::NatsQueue;
use crate::service::im::events::ImEventBus;
use crate::storage::s3::AppS3Storage;
/// Shared infrastructure context for all domain services.
///
/// Each sub-service (Auth, User, Workspace, Repo) holds an `Arc<ServiceContext>`
/// so they share the same database, cache, and other infrastructure without
/// duplicating ownership.
#[derive(Clone)]
pub struct ServiceContext {
pub version: String,
pub db: AppDatabase,
pub redis: AppRedis,
pub cache: Arc<AppCache>,
pub config: AppConfig,
pub storage: AppS3Storage,
/// etcd-based service registry for discovering git and mail RPC services.
pub registry: Arc<EtcdRegistry>,
/// NATS JetStream queue for real-time event broadcasting.
pub nats: Arc<NatsQueue>,
pub im_events: Arc<ImEventBus>,
}
impl ServiceContext {
/// Run a block of work inside a database transaction.
///
/// - Begins a transaction on the writer pool
/// - Sets `app.current_user_id` for RLS / audit triggers
/// - Commits on success, rolls back on error
pub async fn run_in_transaction<F, Fut, T>(&self, user_uid: Uuid, f: F) -> Result<T, AppError>
where
F: FnOnce(&mut sqlx::Transaction<'_, sqlx::Postgres>) -> Fut,
Fut: std::future::Future<Output = Result<T, AppError>>,
{
let mut txn = self
.db
.writer()
.begin()
.await
.map_err(|_| AppError::TxnError)?;
sqlx::query("SET LOCAL app.current_user_id = $1")
.bind(user_uid)
.execute(&mut *txn)
.await
.map_err(AppError::Database)?;
let result = f(&mut txn).await?;
txn.commit().await.map_err(|_| AppError::TxnError)?;
Ok(result)
}
}