use anyhow::{Context as _, Result}; use askama::Template; use axum::{ extract::State, http::{header::CACHE_CONTROL, HeaderValue}, response::{Html, IntoResponse as _, Response}, routing::get, Router, }; use catalogs::{ pg_class::{self, PgClass}, pg_namespace, pg_roles::{self, PgRole}, table_privileges::{self, TablePrivilege}, }; use diesel::{prelude::*, sql_query}; use tower::ServiceBuilder; use tower_http::{ services::{ServeDir, ServeFile}, set_header::SetResponseHeaderLayer, }; use crate::{ abstract_::{class_privileges_for_grantees, escape_identifier}, app_error::AppError, app_state::{AppState, DbConn}, auth, settings::Settings, users::CurrentUser, }; pub fn new_router(state: AppState) -> Router<()> { let base_path = state.settings.base_path.clone(); let app = Router::new() .route("/", get(landing_page)) .nest("/auth", auth::new_router()) .layer(SetResponseHeaderLayer::if_not_present( CACHE_CONTROL, HeaderValue::from_static("no-cache"), )) .fallback_service( ServiceBuilder::new() .layer(SetResponseHeaderLayer::if_not_present( CACHE_CONTROL, HeaderValue::from_static("max-age=21600, stale-while-revalidate=86400"), )) .service( ServeDir::new("static").not_found_service( ServiceBuilder::new() .layer(SetResponseHeaderLayer::if_not_present( CACHE_CONTROL, HeaderValue::from_static("no-cache"), )) .service(ServeFile::new("static/_404.html")), ), ), ) .with_state(state); if base_path.is_empty() { app } else { Router::new().nest(&base_path, app).fallback_service( ServeDir::new("static").not_found_service(ServeFile::new("static/_404.html")), ) } } async fn landing_page( State(Settings { base_path, pg_user_role_prefix, .. }): State, DbConn(db_conn): DbConn, CurrentUser(current_user): CurrentUser, ) -> Result { let grantees = vec![format!( "{}{}", pg_user_role_prefix, current_user.id.simple() )]; let visible_tables = db_conn .interact(move |conn| -> Result> { let role = PgRole::all() .filter(pg_roles::dsl::rolname.eq(format!( "{}{}", pg_user_role_prefix, current_user.id.simple() ))) .first(conn) .optional() .context("error reading role")?; if role.is_none() { sql_query(format!( "CREATE ROLE {}", escape_identifier(&format!( "{}{}", pg_user_role_prefix, current_user.id.simple() )) )) .execute(conn) .context("error creating role")?; } sql_query(format!( "SET ROLE {}", escape_identifier(&format!( "{}{}", pg_user_role_prefix, current_user.id.simple() )) )) .execute(conn) .context("error setting role to user")?; let privileges = class_privileges_for_grantees(grantees) .load(conn) .context("error reading classes")?; Ok(privileges.into_iter().map(|value| value.class).collect()) }) .await .unwrap()?; #[derive(Template)] #[template(path = "tmp.html")] struct ResponseTemplate { base_path: String, relations: Vec, } Ok(Html( ResponseTemplate { base_path, relations: visible_tables, } .render()?, ) .into_response()) }