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    pub fn new(
59        db_pool: Pool<Any>,
60        github_client: GithubApiClient,
61        repo_config: RepoConfig,
62        webhook_secret: WebhookSecret,
63        llm_evaluator: Arc<dyn LlmEvaluator>,
64        max_concurrent_llm_evals: usize,
65        oauth_config: OAuthConfig,
66        config_cache_ttl_seconds: u64,
67    ) -> Self {
68        let github_client_arc = Arc::new(github_client);
69        let repo_config_loader = Arc::new(RepoConfigLoader::new(
70            github_client_arc.clone(),
71            config_cache_ttl_seconds,
72        ));
73
74        Self {
75            db_pool,
76            github_client: github_client_arc,
77            repo_config,
78            webhook_secret,
79            llm_evaluator,
80            llm_semaphore: Arc::new(Semaphore::new(max_concurrent_llm_evals)),
81            oauth_config,
82            repo_config_loader,
83        }
84    }
85}
86
87/// Implement FromRef to allow VerifiedWebhook extractor to access WebhookSecret
88impl FromRef<AppState> for WebhookSecret {
89    fn from_ref(state: &AppState) -> Self {
90        state.webhook_secret.clone()
91    }
92}
93
94/// Implement FromRef to allow OAuth handlers to access OAuthConfig
95impl FromRef<AppState> for OAuthConfig {
96    fn from_ref(state: &AppState) -> Self {
97        state.oauth_config.clone()
98    }
99}
100
101/// Implement FromRef to allow auth middleware to access GithubApiClient
102impl FromRef<AppState> for Arc<GithubApiClient> {
103    fn from_ref(state: &AppState) -> Self {
104        state.github_client.clone()
105    }
106}