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::service::util::set_local_user_id; use crate::storage::s3::AppS3Storage; /// Shared infrastructure context for all domain services. /// /// Each sub-service (Auth, User, Workspace, Repo) holds an `Arc` /// 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, pub config: AppConfig, pub storage: AppS3Storage, /// etcd-based service registry for discovering git and mail RPC services. pub registry: Arc, /// NATS JetStream queue for real-time event broadcasting. pub nats: Arc, pub im_events: Arc, } 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(&self, user_uid: Uuid, f: F) -> Result where F: FnOnce(&mut sqlx::Transaction<'_, sqlx::Postgres>) -> Fut, Fut: std::future::Future>, { let mut txn = self .db .writer() .begin() .await .map_err(|_| AppError::TxnError)?; sqlx::query(set_local_user_id(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) } }