Skip to main content

meritocrab_api/
state.rs

1use crate::repo_config_loader::RepoConfigLoader;
2use axum::extract::FromRef;
3use meritocrab_core::RepoConfig;
4use meritocrab_github::{GithubApiClient, WebhookSecret};
5use meritocrab_llm::LlmEvaluator;
6use serde::{Deserialize, Serialize};
7use sqlx::{Any, Pool};
8use std::sync::Arc;
9use tokio::sync::Semaphore;
10
11/// OAuth configuration
12#[derive(Clone, Debug, Serialize, Deserialize)]
13pub struct OAuthConfig {
14    pub client_id: String,
15    pub client_secret: String,
16    pub redirect_url: String,
17}
18
19/// Application state for Axum dependency injection
20///
21/// This is the DI root that contains all shared resources needed by handlers:
22/// - Database connection pool
23/// - GitHub API client
24/// - Repository configuration
25/// - Webhook secret for HMAC verification
26/// - LLM evaluator for content quality assessment
27/// - Semaphore for limiting concurrent LLM evaluations
28/// - OAuth configuration for admin authentication
29#[derive(Clone)]
30pub struct AppState {
31    /// Database connection pool
32    pub db_pool: Pool<Any>,
33
34    /// GitHub API client for operations like closing PRs
35    pub github_client: Arc<GithubApiClient>,
36
37    /// Repository credit configuration
38    pub repo_config: RepoConfig,
39
40    /// Webhook secret for HMAC verification
41    pub webhook_secret: WebhookSecret,
42
43    /// LLM evaluator for content quality assessment
44    pub llm_evaluator: Arc<dyn LlmEvaluator>,
45
46    /// Semaphore for limiting concurrent LLM evaluations
47    pub llm_semaphore: Arc<Semaphore>,
48
49    /// OAuth configuration for admin authentication
50    pub oauth_config: OAuthConfig,
51
52    /// Repository configuration loader with caching
53    pub repo_config_loader: Arc<RepoConfigLoader>,
54}
55
56impl AppState {
57    /// Create new application state
58    #[allow(clippy::too_many_arguments)]
59    pub fn new(
60        db_pool: Pool<Any>,
61        github_client: GithubApiClient,
62        repo_config: RepoConfig,
63        webhook_secret: WebhookSecret,
64        llm_evaluator: Arc<dyn LlmEvaluator>,
65        max_concurrent_llm_evals: usize,
66        oauth_config: OAuthConfig,
67        config_cache_ttl_seconds: u64,
68    ) -> Self {
69        let github_client_arc = Arc::new(github_client);
70        let repo_config_loader = Arc::new(RepoConfigLoader::new(
71            github_client_arc.clone(),
72            config_cache_ttl_seconds,
73        ));
74
75        Self {
76            db_pool,
77            github_client: github_client_arc,
78            repo_config,
79            webhook_secret,
80            llm_evaluator,
81            llm_semaphore: Arc::new(Semaphore::new(max_concurrent_llm_evals)),
82            oauth_config,
83            repo_config_loader,
84        }
85    }
86}
87
88/// Implement FromRef to allow VerifiedWebhook extractor to access WebhookSecret
89impl FromRef<AppState> for WebhookSecret {
90    fn from_ref(state: &AppState) -> Self {
91        state.webhook_secret.clone()
92    }
93}
94
95/// Implement FromRef to allow OAuth handlers to access OAuthConfig
96impl FromRef<AppState> for OAuthConfig {
97    fn from_ref(state: &AppState) -> Self {
98        state.oauth_config.clone()
99    }
100}
101
102/// Implement FromRef to allow auth middleware to access GithubApiClient
103impl FromRef<AppState> for Arc<GithubApiClient> {
104    fn from_ref(state: &AppState) -> Self {
105        state.github_client.clone()
106    }
107}