Files
Genarrative/server-rs/crates/api-server/src/frontend_runtime_config.rs
T
kdletters e5460280ce
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
发布入口接入灰度开关
- 后端:GET /api/runtime/frontend-config 新增 gameDistributionPublishEnabled,复用 `game-distribution:publish` 判据(未配置或 enabled=false 时对已登录作者默认开放,显式收紧后只放行白名单/灰度命中用户,匿名恒为 false),前端入口与写入口共用同一事实源。
- 后端用例:新增 frontend_runtime_config_game_distribution_publish_is_scoped_to_authenticated_gate,覆盖默认开放、enabled=true 无白名单、白名单命中、deny 名单、enabled=false 回退与 rolloutPercent=100。
- 网页:平台壳按灰度隐藏「发布游戏 / 发布新版本」入口;/games/publish 直接访问时渲染「发布功能正在灰度中」并提供重新检查;读取失败按放行处理,由后端写入口把关并返回可读文案。
- AGC:新增 readGamePublishAvailability(同一运行时配置字段),只有命中才把发布回调交给 DirectProject 聊天头;字段缺失或读取失败按不开放处理。
- 文档:玩法链路、后端数据契约与实施计划记录灰度口径、入口行为与验证命令。
2026-09-22 17:12:04 +08:00

95 lines
3.3 KiB
Rust

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<AppState>,
Extension(request_context): Extension<RequestContext>,
headers: HeaderMap,
) -> Result<Response, AppError> {
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())
}