feat: add many things

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2026-03-08 23:00:03 +01:00
parent 45353089c2
commit 5a5f9a3003
104 changed files with 23417 additions and 2027 deletions

View File

@@ -8,29 +8,94 @@ use std::net::SocketAddr;
use std::sync::Arc;
use axum::Router;
use axum::extract::State;
use axum::http::StatusCode;
use axum::response::{Html, IntoResponse, Response};
use forage_core::session::{FileSessionStore, SessionStore};
use forage_db::PgSessionStore;
use minijinja::context;
use tower_http::services::ServeDir;
use tower_http::trace::TraceLayer;
use opentelemetry::trace::TracerProvider as _;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use crate::forest_client::GrpcForestClient;
use crate::state::AppState;
use crate::templates::TemplateEngine;
fn init_telemetry() {
let env_filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| "info,h2=warn,tonic=info".into());
let fmt_layer = tracing_subscriber::fmt::layer();
if std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").is_ok() {
// OTLP exporter configured — send spans + logs to collector
let tracer = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.build()
.expect("failed to create OTLP span exporter");
let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
.with_batch_exporter(tracer)
.with_resource(
opentelemetry_sdk::Resource::builder()
.with_service_name(
std::env::var("OTEL_SERVICE_NAME")
.unwrap_or_else(|_| "forage-server".into()),
)
.build(),
)
.build();
let otel_layer = tracing_opentelemetry::layer()
.with_tracer(tracer_provider.tracer("forage-server"));
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.with(otel_layer)
.init();
tracing::info!("OpenTelemetry enabled — exporting to OTLP endpoint");
} else {
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.init();
}
}
async fn fallback_404(State(state): State<AppState>) -> Response {
let html = state.templates.render(
"pages/error.html.jinja",
context! {
title => "Not Found - Forage",
description => "The page you're looking for doesn't exist.",
status => 404u16,
heading => "Page not found",
message => "The page you're looking for doesn't exist.",
},
);
match html {
Ok(body) => (StatusCode::NOT_FOUND, Html(body)).into_response(),
Err(_) => StatusCode::NOT_FOUND.into_response(),
}
}
pub fn build_router(state: AppState) -> Router {
Router::new()
.merge(routes::router())
.nest_service("/static", ServeDir::new("static"))
.fallback(fallback_404)
.layer(TraceLayer::new_for_http())
.with_state(state)
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
.init();
init_telemetry();
let forest_endpoint =
std::env::var("FOREST_SERVER_URL").unwrap_or_else(|_| "http://localhost:4040".into());
@@ -81,7 +146,8 @@ async fn main() -> anyhow::Result<()> {
};
let forest_client = Arc::new(forest_client);
let state = AppState::new(template_engine, forest_client.clone(), forest_client, sessions);
let state = AppState::new(template_engine, forest_client.clone(), forest_client.clone(), sessions)
.with_grpc_client(forest_client);
let app = build_router(state);
let port: u16 = std::env::var("PORT")