use axum::{ extract::{Extension, State}, http::{HeaderMap, StatusCode, header}, response::{IntoResponse, Response}, }; use serde::Serialize; use serde_json::json; use crate::{ api_response::json_success_body, auth::optional_access_token_from_headers, http_error::AppError, request_context::RequestContext, state::AppState, }; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct FrontendRuntimeConfigResponse { pub image_editor_agent_sidebar_enabled: bool, pub agc_template_library_enabled: bool, /// 游戏发布灰度:运营未配置 `game-distribution:publish` 时对已登录作者默认开放, /// 显式收紧后只有白名单/灰度命中的作者拿到 `true`;匿名一律 `false`(发布必须先登录)。 pub game_distribution_publish_enabled: bool, } pub async fn get_frontend_runtime_config( State(state): State, Extension(request_context): Extension, headers: HeaderMap, ) -> Result { let authenticated = optional_access_token_from_headers( &state, "/api/runtime/frontend-config".to_string(), headers, request_context.request_id().to_string(), ) .await?; let user_id = authenticated .as_ref() .map(|authenticated| authenticated.claims().user_id()); let image_editor_agent_sidebar_enabled = state .is_image_editor_agent_sidebar_enabled_for_user(user_id) .await .map_err(|error| { AppError::from_status(StatusCode::BAD_GATEWAY) .with_message("读取前端运行时配置失败") .with_details(json!({ "provider": "spacetimedb", "message": error.to_string(), })) })?; let agc_template_library_enabled = state .is_agc_template_library_enabled_for_user(user_id) .await .map_err(|error| { AppError::from_status(StatusCode::BAD_GATEWAY) .with_message("读取前端运行时配置失败") .with_details(json!({ "provider": "spacetimedb", "message": error.to_string(), })) })?; let game_distribution_publish_enabled = match user_id { Some(user_id) => state .is_game_distribution_publish_enabled_for_user(Some(user_id)) .await .map_err(|error| { AppError::from_status(StatusCode::BAD_GATEWAY) .with_message("读取前端运行时配置失败") .with_details(json!({ "provider": "spacetimedb", "message": error.to_string(), })) })?, // 未登录时发布入口不开放:灰度只面向已登录作者,写入口也要求 Bearer 会话。 None => false, }; Ok(( [ (header::CACHE_CONTROL, "no-store"), (header::VARY, "Authorization"), ], json_success_body( Some(&request_context), FrontendRuntimeConfigResponse { image_editor_agent_sidebar_enabled, agc_template_library_enabled, game_distribution_publish_enabled, }, ), ) .into_response()) }