Enrich external API failure audit metadata
This commit is contained in:
@@ -227,3 +227,31 @@ export function parseApiErrorMessage(rawText: string, fallbackMessage: string) {
|
|||||||
|
|
||||||
return rawText.trim() || fallbackMessage;
|
return rawText.trim() || fallbackMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function appendApiErrorRequestId(
|
||||||
|
message: string,
|
||||||
|
requestId: string | null | undefined,
|
||||||
|
) {
|
||||||
|
const trimmedMessage = message.trim() || '请求失败';
|
||||||
|
const trimmedRequestId =
|
||||||
|
typeof requestId === 'string' && requestId.trim()
|
||||||
|
? requestId.trim()
|
||||||
|
: '';
|
||||||
|
|
||||||
|
if (!trimmedRequestId || trimmedMessage.includes(trimmedRequestId)) {
|
||||||
|
return trimmedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${trimmedMessage}(requestId: ${trimmedRequestId})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseApiErrorMessageWithRequestId(
|
||||||
|
rawText: string,
|
||||||
|
fallbackMessage: string,
|
||||||
|
requestId: string | null | undefined,
|
||||||
|
) {
|
||||||
|
return appendApiErrorRequestId(
|
||||||
|
parseApiErrorMessage(rawText, fallbackMessage),
|
||||||
|
requestId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -325,11 +325,15 @@ fn validate_admin_work_visibility(
|
|||||||
) -> Result<(String, String, bool), AppError> {
|
) -> Result<(String, String, bool), AppError> {
|
||||||
let source_type = payload.source_type.trim().to_string();
|
let source_type = payload.source_type.trim().to_string();
|
||||||
if source_type.is_empty() {
|
if source_type.is_empty() {
|
||||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("sourceType 不能为空"));
|
return Err(
|
||||||
|
AppError::from_status(StatusCode::BAD_REQUEST).with_message("sourceType 不能为空")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let profile_id = payload.profile_id.trim().to_string();
|
let profile_id = payload.profile_id.trim().to_string();
|
||||||
if profile_id.is_empty() {
|
if profile_id.is_empty() {
|
||||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("profileId 不能为空"));
|
return Err(
|
||||||
|
AppError::from_status(StatusCode::BAD_REQUEST).with_message("profileId 不能为空")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok((source_type, profile_id, payload.visible))
|
Ok((source_type, profile_id, payload.visible))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -658,7 +658,8 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn spacetime_unavailable_router_returns_service_unavailable_for_requests() {
|
async fn spacetime_unavailable_router_returns_service_unavailable_for_requests() {
|
||||||
let app = build_spacetime_unavailable_router("SpacetimeDB 启动恢复认证快照超时".to_string());
|
let app =
|
||||||
|
build_spacetime_unavailable_router("SpacetimeDB 启动恢复认证快照超时".to_string());
|
||||||
|
|
||||||
let response = app
|
let response = app
|
||||||
.oneshot(
|
.oneshot(
|
||||||
|
|||||||
@@ -311,6 +311,7 @@ pub async fn generate_bark_battle_image_asset(
|
|||||||
async {
|
async {
|
||||||
generate_and_persist_bark_battle_image_asset(
|
generate_and_persist_bark_battle_image_asset(
|
||||||
&state,
|
&state,
|
||||||
|
&request_context,
|
||||||
&owner_user_id,
|
&owner_user_id,
|
||||||
&slot,
|
&slot,
|
||||||
draft_id.as_deref(),
|
draft_id.as_deref(),
|
||||||
@@ -1197,6 +1198,7 @@ fn bark_battle_sanitize_path_segment(value: &str, fallback: &str) -> String {
|
|||||||
|
|
||||||
async fn generate_and_persist_bark_battle_image_asset(
|
async fn generate_and_persist_bark_battle_image_asset(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
slot: &BarkBattleAssetSlot,
|
slot: &BarkBattleAssetSlot,
|
||||||
draft_id: Option<&str>,
|
draft_id: Option<&str>,
|
||||||
@@ -1205,6 +1207,7 @@ async fn generate_and_persist_bark_battle_image_asset(
|
|||||||
size: &str,
|
size: &str,
|
||||||
) -> Result<BarkBattleGeneratedImageAsset, AppError> {
|
) -> Result<BarkBattleGeneratedImageAsset, AppError> {
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
||||||
|
&request_context,
|
||||||
Some(owner_user_id.to_string()),
|
Some(owner_user_id.to_string()),
|
||||||
Some(draft_id.unwrap_or(asset_id).to_string()),
|
Some(draft_id.unwrap_or(asset_id).to_string()),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -95,8 +95,12 @@ pub async fn generate_character_visual(
|
|||||||
|
|
||||||
let result = async {
|
let result = async {
|
||||||
let settings = require_openai_image_settings(&state)?
|
let settings = require_openai_image_settings(&state)?
|
||||||
.with_external_api_audit_context(Some(owner_user_id.clone()), Some(character_id.clone()))
|
.with_external_api_audit_context(
|
||||||
.with_external_api_audit_request_id(Some(request_context.request_id().to_string()));
|
&request_context,
|
||||||
|
Some(owner_user_id.clone()),
|
||||||
|
Some(character_id.clone()),
|
||||||
|
)
|
||||||
|
;
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
|
|
||||||
state
|
state
|
||||||
@@ -320,7 +324,7 @@ pub(crate) async fn generate_character_primary_visual_for_profile(
|
|||||||
&model,
|
&model,
|
||||||
&prompt,
|
&prompt,
|
||||||
)?;
|
)?;
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?.with_external_api_audit_actor(
|
||||||
Some(owner_user_id.to_string()),
|
Some(owner_user_id.to_string()),
|
||||||
Some(character_id.clone()),
|
Some(character_id.clone()),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -555,10 +555,10 @@ pub async fn generate_custom_world_scene_image(
|
|||||||
async {
|
async {
|
||||||
let settings = require_openai_image_settings(&state)?
|
let settings = require_openai_image_settings(&state)?
|
||||||
.with_external_api_audit_context(
|
.with_external_api_audit_context(
|
||||||
|
&request_context,
|
||||||
Some(owner_user_id.to_string()),
|
Some(owner_user_id.to_string()),
|
||||||
normalized.profile_id.clone(),
|
normalized.profile_id.clone(),
|
||||||
)
|
);
|
||||||
.with_external_api_audit_request_id(Some(request_context.request_id().to_string()));
|
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
let reference_image =
|
let reference_image =
|
||||||
if let Some(reference_image_src) = normalized.reference_image_src.as_deref() {
|
if let Some(reference_image_src) = normalized.reference_image_src.as_deref() {
|
||||||
@@ -680,7 +680,7 @@ pub(crate) async fn generate_custom_world_scene_image_for_profile(
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
let normalized = normalize_scene_image_request(payload)?;
|
let normalized = normalize_scene_image_request(payload)?;
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?.with_external_api_audit_actor(
|
||||||
Some(owner_user_id.to_string()),
|
Some(owner_user_id.to_string()),
|
||||||
normalized.profile_id.clone(),
|
normalized.profile_id.clone(),
|
||||||
);
|
);
|
||||||
@@ -1021,10 +1021,10 @@ pub async fn generate_custom_world_opening_cg(
|
|||||||
async {
|
async {
|
||||||
let image_settings = require_openai_image_settings(&state)?
|
let image_settings = require_openai_image_settings(&state)?
|
||||||
.with_external_api_audit_context(
|
.with_external_api_audit_context(
|
||||||
|
&request_context,
|
||||||
Some(owner_user_id.clone()),
|
Some(owner_user_id.clone()),
|
||||||
normalized.profile_id.clone(),
|
normalized.profile_id.clone(),
|
||||||
)
|
);
|
||||||
.with_external_api_audit_request_id(Some(request_context.request_id().to_string()));
|
|
||||||
let image_http_client = build_openai_image_http_client(&image_settings)?;
|
let image_http_client = build_openai_image_http_client(&image_settings)?;
|
||||||
let video_settings = require_ark_video_settings(&state)?;
|
let video_settings = require_ark_video_settings(&state)?;
|
||||||
let video_http_client = build_upstream_http_client(video_settings.request_timeout_ms)?;
|
let video_http_client = build_upstream_http_client(video_settings.request_timeout_ms)?;
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ pub(super) async fn generate_opening_cg_storyboard(
|
|||||||
normalized: &NormalizedOpeningCgRequest,
|
normalized: &NormalizedOpeningCgRequest,
|
||||||
reference_images: &[String],
|
reference_images: &[String],
|
||||||
) -> Result<GeneratedOpeningCgStoryboard, AppError> {
|
) -> Result<GeneratedOpeningCgStoryboard, AppError> {
|
||||||
let audit_settings = settings.clone().with_external_api_audit_context(
|
let audit_settings = settings.clone();
|
||||||
Some(owner_user_id.to_string()),
|
|
||||||
normalized.profile_id.clone(),
|
|
||||||
);
|
|
||||||
let generated = create_openai_image_generation(
|
let generated = create_openai_image_generation(
|
||||||
http_client,
|
http_client,
|
||||||
&audit_settings,
|
&audit_settings,
|
||||||
|
|||||||
@@ -260,13 +260,28 @@ fn build_external_api_failure_metadata(failure: &ExternalApiFailureDraft) -> Val
|
|||||||
if let Some(image_model) = failure.image_model {
|
if let Some(image_model) = failure.image_model {
|
||||||
metadata["imageModel"] = json!(image_model);
|
metadata["imageModel"] = json!(image_model);
|
||||||
}
|
}
|
||||||
if let Some(user_id) = failure.user_id.as_deref().map(str::trim).filter(|value| !value.is_empty()) {
|
if let Some(user_id) = failure
|
||||||
|
.user_id
|
||||||
|
.as_deref()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
{
|
||||||
metadata["userId"] = json!(truncate_field(user_id, 1_000));
|
metadata["userId"] = json!(truncate_field(user_id, 1_000));
|
||||||
}
|
}
|
||||||
if let Some(profile_id) = failure.profile_id.as_deref().map(str::trim).filter(|value| !value.is_empty()) {
|
if let Some(profile_id) = failure
|
||||||
|
.profile_id
|
||||||
|
.as_deref()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
{
|
||||||
metadata["profileId"] = json!(truncate_field(profile_id, 1_000));
|
metadata["profileId"] = json!(truncate_field(profile_id, 1_000));
|
||||||
}
|
}
|
||||||
if let Some(request_id) = failure.request_id.as_deref().map(str::trim).filter(|value| !value.is_empty()) {
|
if let Some(request_id) = failure
|
||||||
|
.request_id
|
||||||
|
.as_deref()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
{
|
||||||
metadata["requestId"] = json!(truncate_field(request_id, 1_000));
|
metadata["requestId"] = json!(truncate_field(request_id, 1_000));
|
||||||
}
|
}
|
||||||
if let Some(source) = failure
|
if let Some(source) = failure
|
||||||
|
|||||||
@@ -416,14 +416,14 @@ async fn maybe_generate_jump_hop_assets(
|
|||||||
.map(|settings| {
|
.map(|settings| {
|
||||||
settings
|
settings
|
||||||
.with_external_api_audit_context(
|
.with_external_api_audit_context(
|
||||||
|
request_context,
|
||||||
Some(owner_user_id.to_string()),
|
Some(owner_user_id.to_string()),
|
||||||
Some(profile_id.clone()),
|
Some(profile_id.clone()),
|
||||||
)
|
)
|
||||||
.with_external_api_audit_request_id(Some(request_context.request_id().to_string()))
|
|
||||||
})
|
})
|
||||||
.map_err(|error| {
|
.map_err(|error| {
|
||||||
jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error)
|
jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error)
|
||||||
})?;
|
})?;
|
||||||
let http_client = build_openai_image_http_client(&settings).map_err(|error| {
|
let http_client = build_openai_image_http_client(&settings).map_err(|error| {
|
||||||
jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error)
|
jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error)
|
||||||
})?;
|
})?;
|
||||||
|
|||||||
@@ -172,7 +172,9 @@ async fn run_server(config: AppConfig) -> Result<(), io::Error> {
|
|||||||
build_spacetime_unavailable_router(message)
|
build_spacetime_unavailable_router(message)
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(std::io::Error::other(format!("初始化应用状态失败:{error}")));
|
return Err(std::io::Error::other(format!(
|
||||||
|
"初始化应用状态失败:{error}"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -701,6 +701,7 @@ pub async fn generate_match3d_cover_image(
|
|||||||
.await?;
|
.await?;
|
||||||
let generated_cover = generate_match3d_cover_image_asset(
|
let generated_cover = generate_match3d_cover_image_asset(
|
||||||
&state,
|
&state,
|
||||||
|
&request_context,
|
||||||
&context.owner_user_id,
|
&context.owner_user_id,
|
||||||
context.session_id.as_str(),
|
context.session_id.as_str(),
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -772,6 +773,7 @@ pub async fn generate_match3d_background_image_for_work(
|
|||||||
async {
|
async {
|
||||||
let generated_background = generate_match3d_background_image(
|
let generated_background = generate_match3d_background_image(
|
||||||
&state,
|
&state,
|
||||||
|
&request_context,
|
||||||
owner_user_id.as_str(),
|
owner_user_id.as_str(),
|
||||||
session_id.as_str(),
|
session_id.as_str(),
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -883,6 +885,7 @@ pub async fn generate_match3d_container_image_for_work(
|
|||||||
async {
|
async {
|
||||||
let generated_container = generate_match3d_container_image(
|
let generated_container = generate_match3d_container_image(
|
||||||
&state,
|
&state,
|
||||||
|
&request_context,
|
||||||
owner_user_id.as_str(),
|
owner_user_id.as_str(),
|
||||||
session_id.as_str(),
|
session_id.as_str(),
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ async fn generate_match3d_item_image_assets_in_batches(
|
|||||||
async move {
|
async move {
|
||||||
let material_sheet = generate_match3d_material_sheet_from_level_scene(
|
let material_sheet = generate_match3d_material_sheet_from_level_scene(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id,
|
owner_user_id,
|
||||||
session_id,
|
session_id,
|
||||||
profile_id,
|
profile_id,
|
||||||
@@ -747,16 +748,19 @@ pub(super) struct Match3DSlicedItemImage {
|
|||||||
|
|
||||||
async fn generate_match3d_material_sheet_from_level_scene(
|
async fn generate_match3d_material_sheet_from_level_scene(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
profile_id: &str,
|
profile_id: &str,
|
||||||
config: &Match3DConfigJson,
|
config: &Match3DConfigJson,
|
||||||
background_asset: Option<&Match3DGeneratedBackgroundAsset>,
|
background_asset: Option<&Match3DGeneratedBackgroundAsset>,
|
||||||
) -> Result<Match3DMaterialSheet, AppError> {
|
) -> Result<Match3DMaterialSheet, AppError> {
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?
|
||||||
Some(owner_user_id.to_string()),
|
.with_external_api_audit_context(
|
||||||
Some(profile_id.to_string()),
|
request_context,
|
||||||
);
|
Some(owner_user_id.to_string()),
|
||||||
|
Some(profile_id.to_string()),
|
||||||
|
);
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
let prompt = build_match3d_item_spritesheet_prompt();
|
let prompt = build_match3d_item_spritesheet_prompt();
|
||||||
let reference = load_match3d_level_scene_reference_image(state, background_asset).await?;
|
let reference = load_match3d_level_scene_reference_image(state, background_asset).await?;
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ pub(super) async fn ensure_match3d_background_asset(
|
|||||||
|
|
||||||
let generated_background = generate_match3d_level_asset_bundle(
|
let generated_background = generate_match3d_level_asset_bundle(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id,
|
owner_user_id,
|
||||||
session_id,
|
session_id,
|
||||||
profile_id,
|
profile_id,
|
||||||
@@ -260,6 +261,7 @@ pub(super) async fn resolve_or_generate_match3d_level_asset_bundle(
|
|||||||
};
|
};
|
||||||
generate_match3d_level_asset_bundle(
|
generate_match3d_level_asset_bundle(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id,
|
owner_user_id,
|
||||||
session_id,
|
session_id,
|
||||||
profile_id,
|
profile_id,
|
||||||
@@ -292,6 +294,7 @@ pub(super) fn build_match3d_item_slug(item_id: &str, item_name: &str) -> String
|
|||||||
|
|
||||||
pub(super) async fn generate_match3d_cover_image_asset(
|
pub(super) async fn generate_match3d_cover_image_asset(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
profile_id: &str,
|
profile_id: &str,
|
||||||
@@ -301,10 +304,12 @@ pub(super) async fn generate_match3d_cover_image_asset(
|
|||||||
reference_image_srcs: Vec<String>,
|
reference_image_srcs: Vec<String>,
|
||||||
) -> Result<Match3DAssetUpload, AppError> {
|
) -> Result<Match3DAssetUpload, AppError> {
|
||||||
require_match3d_oss_client(state)?;
|
require_match3d_oss_client(state)?;
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?
|
||||||
Some(owner_user_id.to_string()),
|
.with_external_api_audit_context(
|
||||||
Some(profile_id.to_string()),
|
request_context,
|
||||||
);
|
Some(owner_user_id.to_string()),
|
||||||
|
Some(profile_id.to_string()),
|
||||||
|
);
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
let cover_prompt = build_match3d_cover_generation_prompt(config, prompt);
|
let cover_prompt = build_match3d_cover_generation_prompt(config, prompt);
|
||||||
let generated = if let Some(uploaded_image) = resolve_match3d_reference_image_for_edit(
|
let generated = if let Some(uploaded_image) = resolve_match3d_reference_image_for_edit(
|
||||||
@@ -425,6 +430,7 @@ pub(super) fn build_match3d_cover_reference_generation_prompt(
|
|||||||
|
|
||||||
pub(super) async fn generate_match3d_background_image(
|
pub(super) async fn generate_match3d_background_image(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
profile_id: &str,
|
profile_id: &str,
|
||||||
@@ -433,6 +439,7 @@ pub(super) async fn generate_match3d_background_image(
|
|||||||
) -> Result<Match3DGeneratedBackgroundAsset, AppError> {
|
) -> Result<Match3DGeneratedBackgroundAsset, AppError> {
|
||||||
generate_match3d_level_asset_bundle(
|
generate_match3d_level_asset_bundle(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id,
|
owner_user_id,
|
||||||
session_id,
|
session_id,
|
||||||
profile_id,
|
profile_id,
|
||||||
@@ -444,6 +451,7 @@ pub(super) async fn generate_match3d_background_image(
|
|||||||
|
|
||||||
pub(super) async fn generate_match3d_level_asset_bundle(
|
pub(super) async fn generate_match3d_level_asset_bundle(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
profile_id: &str,
|
profile_id: &str,
|
||||||
@@ -451,10 +459,12 @@ pub(super) async fn generate_match3d_level_asset_bundle(
|
|||||||
prompt: &str,
|
prompt: &str,
|
||||||
) -> Result<Match3DGeneratedBackgroundAsset, AppError> {
|
) -> Result<Match3DGeneratedBackgroundAsset, AppError> {
|
||||||
require_match3d_oss_client(state)?;
|
require_match3d_oss_client(state)?;
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?
|
||||||
Some(owner_user_id.to_string()),
|
.with_external_api_audit_context(
|
||||||
Some(profile_id.to_string()),
|
request_context,
|
||||||
);
|
Some(owner_user_id.to_string()),
|
||||||
|
Some(profile_id.to_string()),
|
||||||
|
);
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
|
|
||||||
let level_scene_prompt = build_match3d_level_scene_generation_prompt(config);
|
let level_scene_prompt = build_match3d_level_scene_generation_prompt(config);
|
||||||
@@ -589,6 +599,7 @@ pub(super) async fn generate_match3d_level_asset_bundle(
|
|||||||
|
|
||||||
pub(super) async fn generate_match3d_container_image(
|
pub(super) async fn generate_match3d_container_image(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
profile_id: &str,
|
profile_id: &str,
|
||||||
@@ -596,10 +607,12 @@ pub(super) async fn generate_match3d_container_image(
|
|||||||
prompt: &str,
|
prompt: &str,
|
||||||
) -> Result<Match3DGeneratedBackgroundAsset, AppError> {
|
) -> Result<Match3DGeneratedBackgroundAsset, AppError> {
|
||||||
require_match3d_oss_client(state)?;
|
require_match3d_oss_client(state)?;
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?
|
||||||
Some(owner_user_id.to_string()),
|
.with_external_api_audit_context(
|
||||||
Some(profile_id.to_string()),
|
request_context,
|
||||||
);
|
Some(owner_user_id.to_string()),
|
||||||
|
Some(profile_id.to_string()),
|
||||||
|
);
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
let reference_image = load_match3d_container_reference_image()?;
|
let reference_image = load_match3d_container_reference_image()?;
|
||||||
let container_prompt = build_match3d_container_generation_prompt(config, prompt);
|
let container_prompt = build_match3d_container_generation_prompt(config, prompt);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::{
|
|||||||
record_external_api_failure,
|
record_external_api_failure,
|
||||||
},
|
},
|
||||||
http_error::AppError,
|
http_error::AppError,
|
||||||
|
request_context::RequestContext,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
tracking::record_external_generation_run_after_success,
|
tracking::record_external_generation_run_after_success,
|
||||||
};
|
};
|
||||||
@@ -258,7 +259,7 @@ pub(crate) fn build_openai_image_request_body(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OpenAiImageSettings {
|
impl OpenAiImageSettings {
|
||||||
pub(crate) fn with_external_api_audit_context(
|
pub(crate) fn with_external_api_audit_actor(
|
||||||
mut self,
|
mut self,
|
||||||
user_id: Option<String>,
|
user_id: Option<String>,
|
||||||
profile_id: Option<String>,
|
profile_id: Option<String>,
|
||||||
@@ -268,8 +269,15 @@ impl OpenAiImageSettings {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn with_external_api_audit_request_id(mut self, request_id: Option<String>) -> Self {
|
pub(crate) fn with_external_api_audit_context(
|
||||||
self.external_api_audit_request_id = request_id;
|
mut self,
|
||||||
|
request_context: &RequestContext,
|
||||||
|
user_id: Option<String>,
|
||||||
|
profile_id: Option<String>,
|
||||||
|
) -> Self {
|
||||||
|
self.external_api_audit_user_id = user_id;
|
||||||
|
self.external_api_audit_profile_id = profile_id;
|
||||||
|
self.external_api_audit_request_id = Some(request_context.request_id().to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -269,10 +269,10 @@ pub(crate) async fn generate_puzzle_ui_background_image(
|
|||||||
) -> Result<GeneratedPuzzleUiBackgroundResponse, AppError> {
|
) -> Result<GeneratedPuzzleUiBackgroundResponse, AppError> {
|
||||||
let settings = require_openai_image_settings(state.root_state())?
|
let settings = require_openai_image_settings(state.root_state())?
|
||||||
.with_external_api_audit_context(
|
.with_external_api_audit_context(
|
||||||
|
request_context,
|
||||||
Some(owner_user_id.to_string()),
|
Some(owner_user_id.to_string()),
|
||||||
Some(session_id.to_string()),
|
Some(session_id.to_string()),
|
||||||
)
|
);
|
||||||
.with_external_api_audit_request_id(Some(request_context.request_id().to_string()));
|
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
let generated = create_openai_image_generation(
|
let generated = create_openai_image_generation(
|
||||||
&http_client,
|
&http_client,
|
||||||
@@ -311,7 +311,11 @@ pub(crate) async fn generate_puzzle_level_asset_bundle(
|
|||||||
puzzle_image: &PuzzleDownloadedImage,
|
puzzle_image: &PuzzleDownloadedImage,
|
||||||
) -> Result<GeneratedPuzzleLevelAssetBundle, AppError> {
|
) -> Result<GeneratedPuzzleLevelAssetBundle, AppError> {
|
||||||
let settings = require_puzzle_vector_engine_settings(state)?
|
let settings = require_puzzle_vector_engine_settings(state)?
|
||||||
.with_external_api_audit_request_id(Some(request_context.request_id().to_string()));
|
.with_external_api_audit_context(
|
||||||
|
request_context,
|
||||||
|
Some(owner_user_id.to_string()),
|
||||||
|
Some(session_id.to_string()),
|
||||||
|
);
|
||||||
let http_client = build_puzzle_image_http_client(state, PuzzleImageModel::GptImage2)?;
|
let http_client = build_puzzle_image_http_client(state, PuzzleImageModel::GptImage2)?;
|
||||||
let puzzle_reference = build_puzzle_downloaded_image_reference(puzzle_image);
|
let puzzle_reference = build_puzzle_downloaded_image_reference(puzzle_image);
|
||||||
let scene_generated = create_puzzle_vector_engine_image_generation(
|
let scene_generated = create_puzzle_vector_engine_image_generation(
|
||||||
|
|||||||
@@ -109,13 +109,19 @@ impl PuzzleVectorEngineSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn with_external_api_audit_request_id(
|
pub(crate) fn with_external_api_audit_context(
|
||||||
mut self,
|
mut self,
|
||||||
request_id: Option<String>,
|
request_context: &RequestContext,
|
||||||
|
user_id: Option<String>,
|
||||||
|
profile_id: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.external_api_audit_request_id = request_id;
|
self.external_api_audit_user_id = user_id;
|
||||||
|
self.external_api_audit_profile_id = profile_id;
|
||||||
|
self.external_api_audit_request_id =
|
||||||
|
Some(request_context.request_id().to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ParsedPuzzleImageDataUrl {
|
pub(crate) struct ParsedPuzzleImageDataUrl {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ pub(super) async fn generate_square_hole_visual_assets_for_session(
|
|||||||
_ => Some(
|
_ => Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
&owner_user_id,
|
&owner_user_id,
|
||||||
&session_id,
|
&session_id,
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -90,6 +91,7 @@ pub(super) async fn generate_square_hole_visual_assets_for_session(
|
|||||||
_ => Some(
|
_ => Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
&owner_user_id,
|
&owner_user_id,
|
||||||
&session_id,
|
&session_id,
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -118,6 +120,7 @@ pub(super) async fn generate_square_hole_visual_assets_for_session(
|
|||||||
option.image_src = Some(
|
option.image_src = Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
&owner_user_id,
|
&owner_user_id,
|
||||||
&session_id,
|
&session_id,
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -145,6 +148,7 @@ pub(super) async fn generate_square_hole_visual_assets_for_session(
|
|||||||
option.image_src = Some(
|
option.image_src = Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
&owner_user_id,
|
&owner_user_id,
|
||||||
&session_id,
|
&session_id,
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -252,6 +256,7 @@ pub(super) async fn regenerate_square_hole_visual_asset_for_work(
|
|||||||
work.cover_image_src = Some(
|
work.cover_image_src = Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id.as_str(),
|
owner_user_id.as_str(),
|
||||||
synthetic_session_id.as_str(),
|
synthetic_session_id.as_str(),
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -271,6 +276,7 @@ pub(super) async fn regenerate_square_hole_visual_asset_for_work(
|
|||||||
work.background_image_src = Some(
|
work.background_image_src = Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id.as_str(),
|
owner_user_id.as_str(),
|
||||||
synthetic_session_id.as_str(),
|
synthetic_session_id.as_str(),
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -301,6 +307,7 @@ pub(super) async fn regenerate_square_hole_visual_asset_for_work(
|
|||||||
option.image_src = Some(
|
option.image_src = Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id.as_str(),
|
owner_user_id.as_str(),
|
||||||
synthetic_session_id.as_str(),
|
synthetic_session_id.as_str(),
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -331,6 +338,7 @@ pub(super) async fn regenerate_square_hole_visual_asset_for_work(
|
|||||||
option.image_src = Some(
|
option.image_src = Some(
|
||||||
generate_square_hole_image_data_url(
|
generate_square_hole_image_data_url(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id.as_str(),
|
owner_user_id.as_str(),
|
||||||
synthetic_session_id.as_str(),
|
synthetic_session_id.as_str(),
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -380,6 +388,7 @@ pub(super) async fn regenerate_square_hole_visual_asset_for_work(
|
|||||||
|
|
||||||
async fn generate_square_hole_image_data_url(
|
async fn generate_square_hole_image_data_url(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
profile_id: &str,
|
profile_id: &str,
|
||||||
@@ -389,10 +398,12 @@ async fn generate_square_hole_image_data_url(
|
|||||||
size: &str,
|
size: &str,
|
||||||
failure_context: &str,
|
failure_context: &str,
|
||||||
) -> Result<String, AppError> {
|
) -> Result<String, AppError> {
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?
|
||||||
Some(owner_user_id.to_string()),
|
.with_external_api_audit_context(
|
||||||
Some(profile_id.to_string()),
|
request_context,
|
||||||
);
|
Some(owner_user_id.to_string()),
|
||||||
|
Some(profile_id.to_string()),
|
||||||
|
);
|
||||||
let http_client = build_openai_image_http_client(&settings)?;
|
let http_client = build_openai_image_http_client(&settings)?;
|
||||||
let generated = create_openai_image_generation(
|
let generated = create_openai_image_generation(
|
||||||
&http_client,
|
&http_client,
|
||||||
|
|||||||
@@ -1042,7 +1042,9 @@ impl fmt::Display for AppStateInitError {
|
|||||||
match self {
|
match self {
|
||||||
Self::Jwt(error) => write!(f, "{error}"),
|
Self::Jwt(error) => write!(f, "{error}"),
|
||||||
Self::RefreshCookie(error) => write!(f, "{error}"),
|
Self::RefreshCookie(error) => write!(f, "{error}"),
|
||||||
Self::AuthStore(error) | Self::DependencyUnavailable(error) | Self::WechatPay(error) => {
|
Self::AuthStore(error)
|
||||||
|
| Self::DependencyUnavailable(error)
|
||||||
|
| Self::WechatPay(error) => {
|
||||||
write!(f, "{error}")
|
write!(f, "{error}")
|
||||||
}
|
}
|
||||||
Self::SmsProvider(error) => write!(f, "{error}"),
|
Self::SmsProvider(error) => write!(f, "{error}"),
|
||||||
|
|||||||
@@ -526,6 +526,7 @@ async fn maybe_generate_hit_object_asset(
|
|||||||
|
|
||||||
let generated = generate_wooden_fish_image_assets(
|
let generated = generate_wooden_fish_image_assets(
|
||||||
state,
|
state,
|
||||||
|
request_context,
|
||||||
owner_user_id,
|
owner_user_id,
|
||||||
session_id,
|
session_id,
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
@@ -659,6 +660,7 @@ struct WoodenFishGeneratedImageAssets {
|
|||||||
|
|
||||||
async fn generate_wooden_fish_image_assets(
|
async fn generate_wooden_fish_image_assets(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
|
request_context: &RequestContext,
|
||||||
owner_user_id: &str,
|
owner_user_id: &str,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
profile_id: &str,
|
profile_id: &str,
|
||||||
@@ -666,6 +668,7 @@ async fn generate_wooden_fish_image_assets(
|
|||||||
hit_object_reference_image_src: Option<&str>,
|
hit_object_reference_image_src: Option<&str>,
|
||||||
) -> Result<WoodenFishGeneratedImageAssets, AppError> {
|
) -> Result<WoodenFishGeneratedImageAssets, AppError> {
|
||||||
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
let settings = require_openai_image_settings(state)?.with_external_api_audit_context(
|
||||||
|
request_context,
|
||||||
Some(owner_user_id.to_string()),
|
Some(owner_user_id.to_string()),
|
||||||
Some(profile_id.to_string()),
|
Some(profile_id.to_string()),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -98,7 +98,10 @@ pub fn should_rebind_orphan_work_owner(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
!matches!(auth_user_service.get_user_by_id(&owner_user_id), Ok(Some(_)))
|
!matches!(
|
||||||
|
auth_user_service.get_user_by_id(&owner_user_id),
|
||||||
|
Ok(Some(_))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -137,6 +140,9 @@ mod tests {
|
|||||||
|
|
||||||
assert!(should_rebind_orphan_work_owner(&service, ""));
|
assert!(should_rebind_orphan_work_owner(&service, ""));
|
||||||
assert!(should_rebind_orphan_work_owner(&service, "user_missing"));
|
assert!(should_rebind_orphan_work_owner(&service, "user_missing"));
|
||||||
assert!(!should_rebind_orphan_work_owner(&service, ORPHAN_WORK_OWNER_USER_ID));
|
assert!(!should_rebind_orphan_work_owner(
|
||||||
|
&service,
|
||||||
|
ORPHAN_WORK_OWNER_USER_ID
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -807,12 +807,8 @@ impl AuthUserService {
|
|||||||
display_name: &str,
|
display_name: &str,
|
||||||
public_user_code: &str,
|
public_user_code: &str,
|
||||||
) -> Result<AuthUser, PasswordEntryError> {
|
) -> Result<AuthUser, PasswordEntryError> {
|
||||||
self.store.ensure_orphan_work_owner_user(
|
self.store
|
||||||
user_id,
|
.ensure_orphan_work_owner_user(user_id, username, display_name, public_user_code)
|
||||||
username,
|
|
||||||
display_name,
|
|
||||||
public_user_code,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_user_by_id(&self, user_id: &str) -> Result<Option<AuthUser>, LogoutError> {
|
pub fn get_user_by_id(&self, user_id: &str) -> Result<Option<AuthUser>, LogoutError> {
|
||||||
@@ -1019,18 +1015,14 @@ impl InMemoryAuthStore {
|
|||||||
display_name: &str,
|
display_name: &str,
|
||||||
public_user_code: &str,
|
public_user_code: &str,
|
||||||
) -> Result<AuthUser, PasswordEntryError> {
|
) -> Result<AuthUser, PasswordEntryError> {
|
||||||
let user_id = normalize_required_string(user_id).ok_or_else(|| {
|
let user_id = normalize_required_string(user_id)
|
||||||
PasswordEntryError::Store("孤儿作品占位用户 id 不能为空".to_string())
|
.ok_or_else(|| PasswordEntryError::Store("孤儿作品占位用户 id 不能为空".to_string()))?;
|
||||||
})?;
|
let username = normalize_required_string(username)
|
||||||
let username = normalize_required_string(username).ok_or_else(|| {
|
.ok_or_else(|| PasswordEntryError::Store("孤儿作品占位用户名不能为空".to_string()))?;
|
||||||
PasswordEntryError::Store("孤儿作品占位用户名不能为空".to_string())
|
let display_name = normalize_required_string(display_name)
|
||||||
})?;
|
.ok_or_else(|| PasswordEntryError::Store("孤儿作品占位展示名不能为空".to_string()))?;
|
||||||
let display_name = normalize_required_string(display_name).ok_or_else(|| {
|
let public_user_code = normalize_required_string(public_user_code)
|
||||||
PasswordEntryError::Store("孤儿作品占位展示名不能为空".to_string())
|
.ok_or_else(|| PasswordEntryError::Store("孤儿作品占位陶泥号不能为空".to_string()))?;
|
||||||
})?;
|
|
||||||
let public_user_code = normalize_required_string(public_user_code).ok_or_else(|| {
|
|
||||||
PasswordEntryError::Store("孤儿作品占位陶泥号不能为空".to_string())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut state = self
|
let mut state = self
|
||||||
.inner
|
.inner
|
||||||
|
|||||||
@@ -6,18 +6,17 @@ mod mapper;
|
|||||||
mod telemetry;
|
mod telemetry;
|
||||||
use mapper::*;
|
use mapper::*;
|
||||||
pub use mapper::{
|
pub use mapper::{
|
||||||
AiResultReferenceRecord, AiTaskMutationRecord, AiTaskRecord, AiTaskStageRecord,
|
AdminWorkVisibilityRecord, AiResultReferenceRecord, AiTaskMutationRecord, AiTaskRecord,
|
||||||
AiTextChunkRecord, BarkBattleDraftConfigRecord, BarkBattleRunRecord,
|
AiTaskStageRecord, AiTextChunkRecord, BarkBattleDraftConfigRecord, BarkBattleRunRecord,
|
||||||
BarkBattleRuntimeConfigRecord, BattleStateRecord, BigFishAgentMessageRecord,
|
BarkBattleRuntimeConfigRecord, BattleStateRecord, BigFishAgentMessageRecord,
|
||||||
BigFishAnchorItemRecord, BigFishAnchorPackRecord, BigFishAssetCoverageRecord,
|
BigFishAnchorItemRecord, BigFishAnchorPackRecord, BigFishAssetCoverageRecord,
|
||||||
BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord, BigFishBackgroundBlueprintRecord,
|
BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord, BigFishBackgroundBlueprintRecord,
|
||||||
BigFishDraftCompileRecordInput, BigFishGameDraftRecord, BigFishInputSubmitRecordInput,
|
BigFishDraftCompileRecordInput, BigFishGameDraftRecord, BigFishInputSubmitRecordInput,
|
||||||
BigFishLevelBlueprintRecord, BigFishLikeReportRecordInput, BigFishMessageFinalizeRecordInput,
|
BigFishLevelBlueprintRecord, BigFishLikeReportRecordInput, BigFishMessageFinalizeRecordInput,
|
||||||
BigFishMessageSubmitRecordInput, BigFishPlayReportRecordInput, BigFishRunStartRecordInput,
|
BigFishMessageSubmitRecordInput, BigFishPlayReportRecordInput, BigFishRunStartRecordInput,
|
||||||
AdminWorkVisibilityRecord, BigFishRuntimeEntityRecord, BigFishRuntimeParamsRecord,
|
BigFishRuntimeEntityRecord, BigFishRuntimeParamsRecord, BigFishRuntimeRunRecord,
|
||||||
BigFishRuntimeRunRecord, BigFishSessionCreateRecordInput, BigFishSessionRecord,
|
BigFishSessionCreateRecordInput, BigFishSessionRecord, BigFishVector2Record,
|
||||||
BigFishVector2Record, BigFishWorkRemixRecordInput, BigFishWorkSummaryRecord,
|
BigFishWorkRemixRecordInput, BigFishWorkSummaryRecord, CreationEntryConfigRecord,
|
||||||
CreationEntryConfigRecord,
|
|
||||||
CustomWorldAgentActionExecuteRecord, CustomWorldAgentActionExecuteRecordInput,
|
CustomWorldAgentActionExecuteRecord, CustomWorldAgentActionExecuteRecordInput,
|
||||||
CustomWorldAgentCheckpointRecord, CustomWorldAgentMessageFinalizeRecordInput,
|
CustomWorldAgentCheckpointRecord, CustomWorldAgentMessageFinalizeRecordInput,
|
||||||
CustomWorldAgentMessageRecord, CustomWorldAgentMessageSubmitRecordInput,
|
CustomWorldAgentMessageRecord, CustomWorldAgentMessageSubmitRecordInput,
|
||||||
|
|||||||
@@ -115,9 +115,8 @@ pub use self::puzzle::{
|
|||||||
PuzzleWorkProfileRecord, PuzzleWorkRemixRecordInput, PuzzleWorkUpsertRecordInput,
|
PuzzleWorkProfileRecord, PuzzleWorkRemixRecordInput, PuzzleWorkUpsertRecordInput,
|
||||||
};
|
};
|
||||||
pub use self::runtime::{
|
pub use self::runtime::{
|
||||||
AdminWorkVisibilityRecord,
|
AdminWorkVisibilityRecord, BigFishGameDraftRecord, BigFishRuntimeEntityRecord,
|
||||||
BigFishGameDraftRecord, BigFishRuntimeEntityRecord, BigFishRuntimeParamsRecord,
|
BigFishRuntimeParamsRecord, BigFishRuntimeRunRecord, CreationEntryConfigRecord,
|
||||||
BigFishRuntimeRunRecord, CreationEntryConfigRecord,
|
|
||||||
};
|
};
|
||||||
pub use self::runtime_profile::{
|
pub use self::runtime_profile::{
|
||||||
SquareHoleDropConfirmationRecord, SquareHoleDropFeedbackRecord, SquareHoleRunRecord,
|
SquareHoleDropConfirmationRecord, SquareHoleDropFeedbackRecord, SquareHoleRunRecord,
|
||||||
|
|||||||
@@ -97,14 +97,15 @@ impl SpacetimeClient {
|
|||||||
.into();
|
.into();
|
||||||
|
|
||||||
self.call_after_connect("admin_list_work_visibility", move |connection, sender| {
|
self.call_after_connect("admin_list_work_visibility", move |connection, sender| {
|
||||||
connection
|
connection.procedures().admin_list_work_visibility_then(
|
||||||
.procedures()
|
procedure_input,
|
||||||
.admin_list_work_visibility_then(procedure_input, move |_, result| {
|
move |_, result| {
|
||||||
let mapped = result
|
let mapped = result
|
||||||
.map_err(SpacetimeClientError::from_sdk_error)
|
.map_err(SpacetimeClientError::from_sdk_error)
|
||||||
.and_then(map_admin_work_visibility_list_procedure_result);
|
.and_then(map_admin_work_visibility_list_procedure_result);
|
||||||
send_once(&sender, mapped);
|
send_once(&sender, mapped);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -126,19 +127,17 @@ impl SpacetimeClient {
|
|||||||
.map_err(SpacetimeClientError::validation_failed)?
|
.map_err(SpacetimeClientError::validation_failed)?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
self.call_after_connect(
|
self.call_after_connect("admin_update_work_visibility", move |connection, sender| {
|
||||||
"admin_update_work_visibility",
|
connection.procedures().admin_update_work_visibility_then(
|
||||||
move |connection, sender| {
|
procedure_input,
|
||||||
connection
|
move |_, result| {
|
||||||
.procedures()
|
let mapped = result
|
||||||
.admin_update_work_visibility_then(procedure_input, move |_, result| {
|
.map_err(SpacetimeClientError::from_sdk_error)
|
||||||
let mapped = result
|
.and_then(map_admin_work_visibility_procedure_result);
|
||||||
.map_err(SpacetimeClientError::from_sdk_error)
|
send_once(&sender, mapped);
|
||||||
.and_then(map_admin_work_visibility_procedure_result);
|
},
|
||||||
send_once(&sender, mapped);
|
);
|
||||||
});
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,10 @@ fn upsert_auth_snapshot_row(
|
|||||||
.find(&snapshot_id)
|
.find(&snapshot_id)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
ctx.db.auth_store_snapshot().snapshot_id().delete(&snapshot_id);
|
ctx.db
|
||||||
|
.auth_store_snapshot()
|
||||||
|
.snapshot_id()
|
||||||
|
.delete(&snapshot_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.db.auth_store_snapshot().insert(AuthStoreSnapshot {
|
ctx.db.auth_store_snapshot().insert(AuthStoreSnapshot {
|
||||||
@@ -106,7 +109,10 @@ fn auth_store_snapshot_wechat_row_id(provider_uid: &str, user_id: &str) -> Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn auth_store_snapshot_union_row_id(union_id: &str, user_id: &str) -> String {
|
fn auth_store_snapshot_union_row_id(union_id: &str, user_id: &str) -> String {
|
||||||
prefixed_snapshot_id(AUTH_STORE_SNAPSHOT_UNION_PREFIX, &format!("{union_id}|{user_id}"))
|
prefixed_snapshot_id(
|
||||||
|
AUTH_STORE_SNAPSHOT_UNION_PREFIX,
|
||||||
|
&format!("{union_id}|{user_id}"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot_has_user_rows(snapshot: &PersistentAuthStoreSnapshot) -> bool {
|
fn snapshot_has_user_rows(snapshot: &PersistentAuthStoreSnapshot) -> bool {
|
||||||
@@ -202,13 +208,7 @@ fn import_auth_store_snapshot_json_value_tx(
|
|||||||
for stored_user in parsed.users_by_username.into_values() {
|
for stored_user in parsed.users_by_username.into_values() {
|
||||||
let user = stored_user.user;
|
let user = stored_user.user;
|
||||||
let user_id = user.id.clone();
|
let user_id = user.id.clone();
|
||||||
if ctx
|
if ctx.db.user_account().user_id().find(&user_id).is_some() {
|
||||||
.db
|
|
||||||
.user_account()
|
|
||||||
.user_id()
|
|
||||||
.find(&user_id)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
ctx.db.user_account().user_id().delete(&user_id);
|
ctx.db.user_account().user_id().delete(&user_id);
|
||||||
}
|
}
|
||||||
ctx.db.user_account().insert(UserAccount {
|
ctx.db.user_account().insert(UserAccount {
|
||||||
@@ -644,10 +644,7 @@ mod tests {
|
|||||||
|
|
||||||
PersistentAuthStoreSnapshot {
|
PersistentAuthStoreSnapshot {
|
||||||
next_user_id: 43,
|
next_user_id: 43,
|
||||||
users_by_username: std::collections::HashMap::from([(
|
users_by_username: std::collections::HashMap::from([("phone_42".to_string(), user)]),
|
||||||
"phone_42".to_string(),
|
|
||||||
user,
|
|
||||||
)]),
|
|
||||||
phone_to_user_id: std::collections::HashMap::from([(
|
phone_to_user_id: std::collections::HashMap::from([(
|
||||||
"+8613800008000".to_string(),
|
"+8613800008000".to_string(),
|
||||||
"user_00000042".to_string(),
|
"user_00000042".to_string(),
|
||||||
|
|||||||
@@ -2603,7 +2603,7 @@ fn is_same_agent_draft_profile_candidate(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
row.owner_user_id == owner_user_id
|
row.owner_user_id == owner_user_id
|
||||||
&& row.deleted_at.is_none()
|
&& row.deleted_at.is_none()
|
||||||
&& row.visible
|
&& row.visible
|
||||||
&& row.publication_status == CustomWorldPublicationStatus::Draft
|
&& row.publication_status == CustomWorldPublicationStatus::Draft
|
||||||
&& row.source_agent_session_id.as_deref() == Some(source_agent_session_id)
|
&& row.source_agent_session_id.as_deref() == Some(source_agent_session_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use crate::jump_hop::tables::{
|
|||||||
jump_hop_agent_session, jump_hop_event, jump_hop_runtime_run, jump_hop_work_profile,
|
jump_hop_agent_session, jump_hop_event, jump_hop_runtime_run, jump_hop_work_profile,
|
||||||
};
|
};
|
||||||
use crate::match3d::tables::{
|
use crate::match3d::tables::{
|
||||||
match3d_agent_message, match3d_agent_session, match3d_runtime_run, match_3_d_work_profile,
|
match_3_d_work_profile, match3d_agent_message, match3d_agent_session, match3d_runtime_run,
|
||||||
};
|
};
|
||||||
use crate::puzzle::{
|
use crate::puzzle::{
|
||||||
puzzle_agent_message, puzzle_agent_session, puzzle_event, puzzle_leaderboard_entry,
|
puzzle_agent_message, puzzle_agent_session, puzzle_event, puzzle_leaderboard_entry,
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
pub mod analytics_date_dimension;
|
|
||||||
mod admin_work_visibility;
|
mod admin_work_visibility;
|
||||||
|
pub mod analytics_date_dimension;
|
||||||
mod browse_history;
|
mod browse_history;
|
||||||
pub mod creation_entry_config;
|
pub mod creation_entry_config;
|
||||||
mod profile;
|
mod profile;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod snapshots;
|
mod snapshots;
|
||||||
|
|
||||||
pub use analytics_date_dimension::*;
|
|
||||||
pub use admin_work_visibility::*;
|
pub use admin_work_visibility::*;
|
||||||
|
pub use analytics_date_dimension::*;
|
||||||
pub use browse_history::*;
|
pub use browse_history::*;
|
||||||
pub use creation_entry_config::*;
|
pub use creation_entry_config::*;
|
||||||
pub use profile::*;
|
pub use profile::*;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::*;
|
|
||||||
use crate::puzzle::{PuzzleWorkProfileRow, puzzle_work_profile};
|
use crate::puzzle::{PuzzleWorkProfileRow, puzzle_work_profile};
|
||||||
|
use crate::*;
|
||||||
use module_custom_world::CustomWorldPublicationStatus;
|
use module_custom_world::CustomWorldPublicationStatus;
|
||||||
use module_puzzle::PuzzlePublicationStatus;
|
use module_puzzle::PuzzlePublicationStatus;
|
||||||
|
|
||||||
@@ -93,7 +93,9 @@ fn update_work_visibility_tx(
|
|||||||
update_wooden_fish_work_visibility(ctx, &profile_id, input.visible)
|
update_wooden_fish_work_visibility(ctx, &profile_id, input.visible)
|
||||||
}
|
}
|
||||||
SOURCE_TYPE_MATCH3D => update_match3d_work_visibility(ctx, &profile_id, input.visible),
|
SOURCE_TYPE_MATCH3D => update_match3d_work_visibility(ctx, &profile_id, input.visible),
|
||||||
SOURCE_TYPE_SQUARE_HOLE => update_square_hole_work_visibility(ctx, &profile_id, input.visible),
|
SOURCE_TYPE_SQUARE_HOLE => {
|
||||||
|
update_square_hole_work_visibility(ctx, &profile_id, input.visible)
|
||||||
|
}
|
||||||
SOURCE_TYPE_VISUAL_NOVEL => {
|
SOURCE_TYPE_VISUAL_NOVEL => {
|
||||||
update_visual_novel_work_visibility(ctx, &profile_id, input.visible)
|
update_visual_novel_work_visibility(ctx, &profile_id, input.visible)
|
||||||
}
|
}
|
||||||
@@ -158,7 +160,9 @@ fn puzzle_work_visibility_snapshot(row: &PuzzleWorkProfileRow) -> AdminWorkVisib
|
|||||||
subtitle: "拼图关卡".to_string(),
|
subtitle: "拼图关卡".to_string(),
|
||||||
cover_image_src: row.cover_image_src.clone(),
|
cover_image_src: row.cover_image_src.clone(),
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
published_at_micros: row.published_at.map(|value| value.to_micros_since_unix_epoch()),
|
published_at_micros: row
|
||||||
|
.published_at
|
||||||
|
.map(|value| value.to_micros_since_unix_epoch()),
|
||||||
updated_at_micros: sort_time,
|
updated_at_micros: sort_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,7 +238,9 @@ fn custom_world_work_visibility_snapshot(row: &CustomWorldProfile) -> AdminWorkV
|
|||||||
subtitle: row.subtitle.clone(),
|
subtitle: row.subtitle.clone(),
|
||||||
cover_image_src: row.cover_image_src.clone(),
|
cover_image_src: row.cover_image_src.clone(),
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
published_at_micros: row.published_at.map(|value| value.to_micros_since_unix_epoch()),
|
published_at_micros: row
|
||||||
|
.published_at
|
||||||
|
.map(|value| value.to_micros_since_unix_epoch()),
|
||||||
updated_at_micros: sort_time,
|
updated_at_micros: sort_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,7 +293,9 @@ fn jump_hop_work_visibility_snapshot(row: &JumpHopWorkProfileRow) -> AdminWorkVi
|
|||||||
subtitle: "跳一跳".to_string(),
|
subtitle: "跳一跳".to_string(),
|
||||||
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
published_at_micros: row.published_at.map(|value| value.to_micros_since_unix_epoch()),
|
published_at_micros: row
|
||||||
|
.published_at
|
||||||
|
.map(|value| value.to_micros_since_unix_epoch()),
|
||||||
updated_at_micros: sort_time,
|
updated_at_micros: sort_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,7 +350,9 @@ fn wooden_fish_work_visibility_snapshot(
|
|||||||
subtitle: "敲木鱼".to_string(),
|
subtitle: "敲木鱼".to_string(),
|
||||||
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
published_at_micros: row.published_at.map(|value| value.to_micros_since_unix_epoch()),
|
published_at_micros: row
|
||||||
|
.published_at
|
||||||
|
.map(|value| value.to_micros_since_unix_epoch()),
|
||||||
updated_at_micros: sort_time,
|
updated_at_micros: sort_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,7 +405,9 @@ fn match3d_work_visibility_snapshot(row: &Match3DWorkProfileRow) -> AdminWorkVis
|
|||||||
subtitle: "抓大鹅".to_string(),
|
subtitle: "抓大鹅".to_string(),
|
||||||
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
published_at_micros: row.published_at.map(|value| value.to_micros_since_unix_epoch()),
|
published_at_micros: row
|
||||||
|
.published_at
|
||||||
|
.map(|value| value.to_micros_since_unix_epoch()),
|
||||||
updated_at_micros: sort_time,
|
updated_at_micros: sort_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,7 +462,9 @@ fn square_hole_work_visibility_snapshot(
|
|||||||
subtitle: "方洞挑战".to_string(),
|
subtitle: "方洞挑战".to_string(),
|
||||||
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
published_at_micros: row.published_at.map(|value| value.to_micros_since_unix_epoch()),
|
published_at_micros: row
|
||||||
|
.published_at
|
||||||
|
.map(|value| value.to_micros_since_unix_epoch()),
|
||||||
updated_at_micros: sort_time,
|
updated_at_micros: sort_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,7 +519,9 @@ fn visual_novel_work_visibility_snapshot(
|
|||||||
subtitle: "视觉小说".to_string(),
|
subtitle: "视觉小说".to_string(),
|
||||||
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
cover_image_src: Some(row.cover_image_src.clone()).filter(|value| !value.is_empty()),
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
published_at_micros: row.published_at.map(|value| value.to_micros_since_unix_epoch()),
|
published_at_micros: row
|
||||||
|
.published_at
|
||||||
|
.map(|value| value.to_micros_since_unix_epoch()),
|
||||||
updated_at_micros: sort_time,
|
updated_at_micros: sort_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,10 +560,10 @@ fn update_big_fish_work_visibility(
|
|||||||
Ok(snapshot)
|
Ok(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn big_fish_work_visibility_snapshot(
|
fn big_fish_work_visibility_snapshot(row: &BigFishCreationSession) -> AdminWorkVisibilitySnapshot {
|
||||||
row: &BigFishCreationSession,
|
let published_at = row
|
||||||
) -> AdminWorkVisibilitySnapshot {
|
.published_at
|
||||||
let published_at = row.published_at.map(|value| value.to_micros_since_unix_epoch());
|
.map(|value| value.to_micros_since_unix_epoch());
|
||||||
let updated_at = timestamp_sort_micros(row.published_at, row.updated_at);
|
let updated_at = timestamp_sort_micros(row.published_at, row.updated_at);
|
||||||
AdminWorkVisibilitySnapshot {
|
AdminWorkVisibilitySnapshot {
|
||||||
source_type: SOURCE_TYPE_BIG_FISH.to_string(),
|
source_type: SOURCE_TYPE_BIG_FISH.to_string(),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
postApiJson,
|
postApiJson,
|
||||||
} from '../../editor/shared/editorApiClient';
|
} from '../../editor/shared/editorApiClient';
|
||||||
import {
|
import {
|
||||||
|
appendApiErrorRequestId,
|
||||||
fetchJson,
|
fetchJson,
|
||||||
parseApiErrorMessage,
|
parseApiErrorMessage,
|
||||||
} from '../../editor/shared/jsonClient';
|
} from '../../editor/shared/jsonClient';
|
||||||
@@ -265,7 +266,10 @@ export async function putCharacterRoleAssetWorkflow(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
parseApiErrorMessage(responseText, '保存角色资产工坊缓存失败'),
|
appendApiErrorRequestId(
|
||||||
|
parseApiErrorMessage(responseText, '保存角色资产工坊缓存失败'),
|
||||||
|
response.headers.get('x-request-id'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { fetchJson, parseApiErrorMessage, saveJsonObject } from './jsonClient';
|
import {
|
||||||
|
appendApiErrorRequestId,
|
||||||
|
fetchJson,
|
||||||
|
parseApiErrorMessage,
|
||||||
|
saveJsonObject,
|
||||||
|
} from './jsonClient';
|
||||||
|
|
||||||
export const EDITOR_API_BASE_PATH = '/api/editor';
|
export const EDITOR_API_BASE_PATH = '/api/editor';
|
||||||
export const ASSETS_API_BASE_PATH = '/api/assets';
|
export const ASSETS_API_BASE_PATH = '/api/assets';
|
||||||
@@ -69,7 +74,7 @@ export async function postApiJson<T>(
|
|||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(parseApiErrorMessage(responseText, fallbackMessage));
|
throw new Error(appendApiErrorRequestId(parseApiErrorMessage(responseText, fallbackMessage), response.headers.get('x-request-id')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseText ? (JSON.parse(responseText) as T) : ({} as T);
|
return responseText ? (JSON.parse(responseText) as T) : ({} as T);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
API_RESPONSE_ENVELOPE_HEADER,
|
API_RESPONSE_ENVELOPE_HEADER,
|
||||||
API_RESPONSE_ENVELOPE_VERSION,
|
API_RESPONSE_ENVELOPE_VERSION,
|
||||||
|
appendApiErrorRequestId,
|
||||||
parseApiErrorMessage,
|
parseApiErrorMessage,
|
||||||
unwrapApiResponse,
|
unwrapApiResponse,
|
||||||
} from '../../../packages/shared/src/http';
|
} from '../../../packages/shared/src/http';
|
||||||
@@ -17,7 +18,15 @@ export async function fetchJson<T>(
|
|||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(parseApiErrorMessage(responseText, `${fallbackMessage}: ${response.status}`));
|
throw new Error(
|
||||||
|
appendApiErrorRequestId(
|
||||||
|
parseApiErrorMessage(
|
||||||
|
responseText,
|
||||||
|
`${fallbackMessage}: ${response.status}`,
|
||||||
|
),
|
||||||
|
response.headers.get('x-request-id'),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseText
|
return responseText
|
||||||
@@ -41,8 +50,13 @@ export async function saveJsonObject(
|
|||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(parseApiErrorMessage(responseText, fallbackMessage));
|
throw new Error(
|
||||||
|
appendApiErrorRequestId(
|
||||||
|
parseApiErrorMessage(responseText, fallbackMessage),
|
||||||
|
response.headers.get('x-request-id'),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { parseApiErrorMessage };
|
export { appendApiErrorRequestId, parseApiErrorMessage };
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import type {
|
|||||||
GenerateCustomWorldProfileInput,
|
GenerateCustomWorldProfileInput,
|
||||||
GenerateCustomWorldProfileOptions,
|
GenerateCustomWorldProfileOptions,
|
||||||
} from '../../packages/shared/src/contracts/runtime';
|
} from '../../packages/shared/src/contracts/runtime';
|
||||||
import { parseApiErrorMessage } from '../../packages/shared/src/http';
|
import {
|
||||||
|
appendApiErrorRequestId,
|
||||||
|
parseApiErrorMessage,
|
||||||
|
} from '../../packages/shared/src/http';
|
||||||
import type {
|
import type {
|
||||||
AIResponse,
|
AIResponse,
|
||||||
Character,
|
Character,
|
||||||
@@ -93,7 +96,12 @@ async function requestPlainTextStream(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
throw new Error(parseApiErrorMessage(responseText, '流式请求失败'));
|
throw new Error(
|
||||||
|
appendApiErrorRequestId(
|
||||||
|
parseApiErrorMessage(responseText, '流式请求失败'),
|
||||||
|
response.headers.get('x-request-id'),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
@@ -488,7 +496,12 @@ export async function streamNpcChatTurn(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
throw new Error(parseApiErrorMessage(responseText, 'NPC 聊天续写失败'));
|
throw new Error(
|
||||||
|
appendApiErrorRequestId(
|
||||||
|
parseApiErrorMessage(responseText, 'NPC 聊天续写失败'),
|
||||||
|
response.headers.get('x-request-id'),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ describe('apiClient', () => {
|
|||||||
routeVersion: 'runtime.v2',
|
routeVersion: 'runtime.v2',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
expect((capturedError as Error).message).toContain('requestId: req-body');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses api error details.message as ApiClientError message', async () => {
|
it('uses api error details.message as ApiClientError message', async () => {
|
||||||
|
|||||||
@@ -671,9 +671,14 @@ async function buildApiClientError(
|
|||||||
) {
|
) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
const parsedError = parseApiErrorShape(responseText);
|
const parsedError = parseApiErrorShape(responseText);
|
||||||
|
const requestId =
|
||||||
|
parsedError?.meta.requestId ??
|
||||||
|
response.headers.get(REQUEST_ID_HEADER) ??
|
||||||
|
undefined;
|
||||||
|
const baseMessage = parseApiErrorMessage(responseText, fallbackMessage);
|
||||||
|
|
||||||
return new ApiClientError({
|
return new ApiClientError({
|
||||||
message: parseApiErrorMessage(responseText, fallbackMessage),
|
message: requestId ? `${baseMessage}(requestId: ${requestId})` : baseMessage,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
code: parsedError?.code ?? `HTTP_${response.status || 0}`,
|
code: parsedError?.code ?? `HTTP_${response.status || 0}`,
|
||||||
details: parsedError?.details ?? null,
|
details: parsedError?.details ?? null,
|
||||||
@@ -682,10 +687,7 @@ async function buildApiClientError(
|
|||||||
parsedError?.meta.apiVersion ??
|
parsedError?.meta.apiVersion ??
|
||||||
response.headers.get(API_VERSION_HEADER) ??
|
response.headers.get(API_VERSION_HEADER) ??
|
||||||
API_VERSION,
|
API_VERSION,
|
||||||
requestId:
|
requestId,
|
||||||
parsedError?.meta.requestId ??
|
|
||||||
response.headers.get(REQUEST_ID_HEADER) ??
|
|
||||||
undefined,
|
|
||||||
routeVersion:
|
routeVersion:
|
||||||
parsedError?.meta.routeVersion ??
|
parsedError?.meta.routeVersion ??
|
||||||
response.headers.get(ROUTE_VERSION_HEADER) ??
|
response.headers.get(ROUTE_VERSION_HEADER) ??
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { parseApiErrorMessage } from '../../packages/shared/src/http';
|
import {
|
||||||
|
appendApiErrorRequestId,
|
||||||
|
parseApiErrorMessage,
|
||||||
|
} from '../../packages/shared/src/http';
|
||||||
import {
|
import {
|
||||||
ApiClientError,
|
ApiClientError,
|
||||||
BACKGROUND_AUTH_REQUEST_OPTIONS,
|
BACKGROUND_AUTH_REQUEST_OPTIONS,
|
||||||
@@ -338,7 +341,12 @@ export async function readAssetBytes(
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const message = await response
|
const message = await response
|
||||||
.text()
|
.text()
|
||||||
.then((text) => parseApiErrorMessage(text, '读取资源内容失败'))
|
.then((text) =>
|
||||||
|
appendApiErrorRequestId(
|
||||||
|
parseApiErrorMessage(text, '读取资源内容失败'),
|
||||||
|
response.headers.get('x-request-id'),
|
||||||
|
),
|
||||||
|
)
|
||||||
.catch(() => '');
|
.catch(() => '');
|
||||||
throw new Error(message || '读取资源内容失败');
|
throw new Error(message || '读取资源内容失败');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { parseApiErrorMessage } from '../../../packages/shared/src/http';
|
import { appendApiErrorRequestId, parseApiErrorMessage } from '../../../packages/shared/src/http';
|
||||||
import type { TextStreamOptions } from '../aiTypes';
|
import type { TextStreamOptions } from '../aiTypes';
|
||||||
import {
|
import {
|
||||||
type ApiRetryOptions,
|
type ApiRetryOptions,
|
||||||
@@ -64,7 +64,7 @@ async function openCreationAgentSsePost(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
throw new Error(parseApiErrorMessage(responseText, fallbackMessage));
|
throw new Error(appendApiErrorRequestId(parseApiErrorMessage(responseText, fallbackMessage), response.headers.get('x-request-id')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
CreativeDraftEditStreamRequest,
|
CreativeDraftEditStreamRequest,
|
||||||
StreamCreativeAgentMessageRequest,
|
StreamCreativeAgentMessageRequest,
|
||||||
} from '../../../packages/shared/src/contracts/creativeAgent';
|
} from '../../../packages/shared/src/contracts/creativeAgent';
|
||||||
import { parseApiErrorMessage } from '../../../packages/shared/src/http';
|
import { appendApiErrorRequestId, parseApiErrorMessage } from '../../../packages/shared/src/http';
|
||||||
import type { TextStreamOptions } from '../aiTypes';
|
import type { TextStreamOptions } from '../aiTypes';
|
||||||
import { fetchWithApiAuth, requestJson } from '../apiClient';
|
import { fetchWithApiAuth, requestJson } from '../apiClient';
|
||||||
import {
|
import {
|
||||||
@@ -42,7 +42,7 @@ async function openCreativeAgentSsePost(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
throw new Error(parseApiErrorMessage(responseText, fallbackMessage));
|
throw new Error(appendApiErrorRequestId(parseApiErrorMessage(responseText, fallbackMessage), response.headers.get('x-request-id')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { parseApiErrorMessage } from '../../../packages/shared/src/http';
|
import { appendApiErrorRequestId, parseApiErrorMessage } from '../../../packages/shared/src/http';
|
||||||
import { fetchWithApiAuth, requestJson } from '../apiClient';
|
import { fetchWithApiAuth, requestJson } from '../apiClient';
|
||||||
|
|
||||||
export async function requestRpgCreationPostJson<T>(
|
export async function requestRpgCreationPostJson<T>(
|
||||||
@@ -32,7 +32,7 @@ export async function openRpgCreationSsePost(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
throw new Error(parseApiErrorMessage(responseText, fallbackMessage));
|
throw new Error(appendApiErrorRequestId(parseApiErrorMessage(responseText, fallbackMessage), response.headers.get('x-request-id')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import type {
|
|||||||
VisualNovelStartRunRequest,
|
VisualNovelStartRunRequest,
|
||||||
VisualNovelWorksResponse,
|
VisualNovelWorksResponse,
|
||||||
} from '../../../packages/shared/src/contracts/visualNovel';
|
} from '../../../packages/shared/src/contracts/visualNovel';
|
||||||
import { parseApiErrorMessage } from '../../../packages/shared/src/http';
|
import { appendApiErrorRequestId, parseApiErrorMessage } from '../../../packages/shared/src/http';
|
||||||
import type { TextStreamOptions } from '../aiTypes';
|
import type { TextStreamOptions } from '../aiTypes';
|
||||||
import {
|
import {
|
||||||
type ApiRetryOptions,
|
type ApiRetryOptions,
|
||||||
@@ -100,7 +100,7 @@ async function openVisualNovelRuntimeSsePost(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
throw new Error(parseApiErrorMessage(responseText, fallbackMessage));
|
throw new Error(appendApiErrorRequestId(parseApiErrorMessage(responseText, fallbackMessage), response.headers.get('x-request-id')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
|
|||||||
Reference in New Issue
Block a user