This commit is contained in:
2026-05-11 20:27:41 +08:00
parent e30b733b17
commit 481a27fc53
60 changed files with 6357 additions and 1100 deletions

View File

@@ -5,7 +5,7 @@ use axum::{
http::Request,
middleware,
response::Response,
routing::{delete, get, post},
routing::{delete, get, post, put},
};
use tower_http::{
classify::ServerErrorsFailureClass,
@@ -92,8 +92,8 @@ use crate::{
delete_match3d_work, execute_match3d_agent_action, finish_match3d_time_up,
generate_match3d_work_tags, get_match3d_agent_session, get_match3d_run,
get_match3d_work_detail, get_match3d_works, list_match3d_gallery, publish_match3d_work,
put_match3d_work, restart_match3d_run, start_match3d_run, stop_match3d_run,
stream_match3d_agent_message, submit_match3d_agent_message,
put_match3d_audio_assets, put_match3d_work, restart_match3d_run, start_match3d_run,
stop_match3d_run, stream_match3d_agent_message, submit_match3d_agent_message,
},
password_entry::password_entry,
password_management::{change_password, reset_password},
@@ -156,7 +156,9 @@ use crate::{
},
tracking::record_route_tracking_event_after_success,
vector_engine_audio_generation::{
create_background_music_task, create_sound_effect_task,
create_visual_novel_background_music_task, create_visual_novel_sound_effect_task,
publish_background_music_asset, publish_sound_effect_asset,
publish_visual_novel_background_music_asset, publish_visual_novel_sound_effect_asset,
},
visual_novel::{
@@ -942,6 +944,13 @@ pub fn build_router(state: AppState) -> Router {
require_bearer_auth,
)),
)
.route(
"/api/creation/match3d/works/{profile_id}/audio-assets",
put(put_match3d_audio_assets).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/match3d/works/{profile_id}/publish",
post(publish_match3d_work).route_layer(middleware::from_fn_with_state(
@@ -1517,7 +1526,7 @@ pub fn build_router(state: AppState) -> Router {
)),
)
.route("/api/auth/password/reset", post(reset_password))
// 后端 runtime/API 路由只按 open 做熔断visible 仅控制创作页入口展示。
// 后端创作/运行态 API 路由只按 open 做熔断visible 仅控制创作页入口展示。
.layer(middleware::from_fn_with_state(
state.clone(),
require_creation_entry_route_enabled,
@@ -1770,6 +1779,34 @@ fn visual_novel_router(state: AppState) -> Router<AppState> {
middleware::from_fn_with_state(state.clone(), require_bearer_auth),
),
)
.route(
"/api/creation/audio/background-music",
post(create_background_music_task).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/audio/background-music/{task_id}/asset",
post(publish_background_music_asset).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/audio/sound-effect",
post(create_sound_effect_task).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/audio/sound-effect/{task_id}/asset",
post(publish_sound_effect_asset).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/runtime/visual-novel/gallery",
get(list_visual_novel_gallery),
@@ -2007,6 +2044,38 @@ mod tests {
assert_eq!(body["error"]["details"]["creationTypeId"], "puzzle");
}
#[tokio::test]
async fn disabled_visual_novel_creation_route_returns_service_unavailable() {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/api/creation/visual-novel/sessions")
.header("content-type", "application/json")
.body(Body::from(
serde_json::json!({
"sourceMode": "idea",
"seedText": "雨夜书店",
"sourceAssetIds": []
})
.to_string(),
))
.expect("request should build"),
)
.await
.expect("request should succeed");
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
let body = read_json_response(response).await;
assert_eq!(
body["error"]["details"]["reason"],
"creation_entry_disabled"
);
assert_eq!(body["error"]["details"]["creationTypeId"], "visual-novel");
}
#[tokio::test]
async fn healthz_returns_standard_envelope_when_requested() {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
@@ -4693,7 +4762,9 @@ mod tests {
#[tokio::test]
async fn visual_novel_creation_route_requires_authentication() {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
let state = AppState::new(AppConfig::default()).expect("state should build");
state.set_test_creation_entry_route_enabled("visual-novel", true);
let app = build_router(state);
let response = app
.oneshot(