@@ -11,6 +11,7 @@ mod templates;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use forage_core::session::{FileSessionStore, SessionStore};
|
||||
use forage_db::PgSessionStore;
|
||||
@@ -54,8 +55,8 @@ fn init_telemetry() {
|
||||
)
|
||||
.build();
|
||||
|
||||
let otel_layer = tracing_opentelemetry::layer()
|
||||
.with_tracer(tracer_provider.tracer("forage-server"));
|
||||
let otel_layer =
|
||||
tracing_opentelemetry::layer().with_tracer(tracer_provider.tracer("forage-server"));
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
@@ -119,7 +120,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
let mut mad = notmad::Mad::builder();
|
||||
|
||||
// Session store + integration store: PostgreSQL if DATABASE_URL is set
|
||||
let (sessions, integration_store): (Arc<dyn SessionStore>, Option<Arc<dyn forage_core::integrations::IntegrationStore>>);
|
||||
let (sessions, integration_store): (
|
||||
Arc<dyn SessionStore>,
|
||||
Option<Arc<dyn forage_core::integrations::IntegrationStore>>,
|
||||
);
|
||||
|
||||
if let Ok(database_url) = std::env::var("DATABASE_URL") {
|
||||
tracing::info!("using PostgreSQL session store");
|
||||
@@ -129,12 +133,16 @@ async fn main() -> anyhow::Result<()> {
|
||||
let pg_store = Arc::new(PgSessionStore::new(pool.clone()));
|
||||
|
||||
// Integration store (uses same pool)
|
||||
let encryption_key = std::env::var("INTEGRATION_ENCRYPTION_KEY")
|
||||
.unwrap_or_else(|_| {
|
||||
tracing::warn!("INTEGRATION_ENCRYPTION_KEY not set — using default key (not safe for production)");
|
||||
"forage-dev-key-not-for-production!!".to_string()
|
||||
});
|
||||
let pg_integrations = Arc::new(forage_db::PgIntegrationStore::new(pool, encryption_key.into_bytes()));
|
||||
let encryption_key = std::env::var("INTEGRATION_ENCRYPTION_KEY").unwrap_or_else(|_| {
|
||||
tracing::warn!(
|
||||
"INTEGRATION_ENCRYPTION_KEY not set — using default key (not safe for production)"
|
||||
);
|
||||
"forage-dev-key-not-for-production!!".to_string()
|
||||
});
|
||||
let pg_integrations = Arc::new(forage_db::PgIntegrationStore::new(
|
||||
pool,
|
||||
encryption_key.into_bytes(),
|
||||
));
|
||||
|
||||
// Session reaper component
|
||||
mad.add(session_reaper::PgSessionReaper {
|
||||
@@ -143,11 +151,15 @@ async fn main() -> anyhow::Result<()> {
|
||||
});
|
||||
|
||||
sessions = pg_store;
|
||||
integration_store = Some(pg_integrations as Arc<dyn forage_core::integrations::IntegrationStore>);
|
||||
integration_store =
|
||||
Some(pg_integrations as Arc<dyn forage_core::integrations::IntegrationStore>);
|
||||
} else {
|
||||
let session_dir = std::env::var("SESSION_DIR").unwrap_or_else(|_| "target/sessions".into());
|
||||
tracing::info!("using file session store at {session_dir} (set DATABASE_URL for PostgreSQL)");
|
||||
let file_store = Arc::new(FileSessionStore::new(&session_dir).expect("failed to create session dir"));
|
||||
tracing::info!(
|
||||
"using file session store at {session_dir} (set DATABASE_URL for PostgreSQL)"
|
||||
);
|
||||
let file_store =
|
||||
Arc::new(FileSessionStore::new(&session_dir).expect("failed to create session dir"));
|
||||
|
||||
// File session reaper component
|
||||
mad.add(session_reaper::FileSessionReaper {
|
||||
@@ -159,8 +171,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
};
|
||||
|
||||
let forest_client = Arc::new(forest_client);
|
||||
let mut state = AppState::new(template_engine, forest_client.clone(), forest_client.clone(), sessions)
|
||||
.with_grpc_client(forest_client.clone());
|
||||
let mut state = AppState::new(
|
||||
template_engine,
|
||||
forest_client.clone(),
|
||||
forest_client.clone(),
|
||||
sessions,
|
||||
)
|
||||
.with_grpc_client(forest_client.clone());
|
||||
|
||||
// Slack OAuth config (optional, enables "Add to Slack" button)
|
||||
if let (Ok(client_id), Ok(client_secret)) = (
|
||||
@@ -220,7 +237,9 @@ async fn main() -> anyhow::Result<()> {
|
||||
});
|
||||
} else {
|
||||
// Fallback: direct dispatch (no durability)
|
||||
tracing::warn!("NATS_URL not set — using direct notification dispatch (no durability)");
|
||||
tracing::warn!(
|
||||
"NATS_URL not set — using direct notification dispatch (no durability)"
|
||||
);
|
||||
mad.add(notification_worker::NotificationListener {
|
||||
grpc: forest_client,
|
||||
store: store.clone(),
|
||||
@@ -234,12 +253,11 @@ async fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
// HTTP server component
|
||||
mad.add(serve_http::ServeHttp {
|
||||
addr,
|
||||
state,
|
||||
});
|
||||
mad.add(serve_http::ServeHttp { addr, state });
|
||||
|
||||
mad.run().await?;
|
||||
mad.cancellation(Some(Duration::from_secs(10)))
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user