新增画布完美像素功能
在图片工具栏接入完美像素交互、占位流程与历史保护 新增严格像素网格检测和登录态同步处理接口 补齐素材类型校验、持久化边界、并发限制及技术文档 修复 Windows rlib 门禁长文件名解析
This commit is contained in:
@@ -1963,6 +1963,147 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn editor_pixel_art_snap_requires_bearer_auth() {
|
||||
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
|
||||
let request_body = serde_json::json!({
|
||||
"sourceImageSrc": "editor-resource-source",
|
||||
"projectId": "proj-source",
|
||||
"canvasCompletion": {
|
||||
"dialogId": "dialog-pixel-art",
|
||||
"title": "完美像素",
|
||||
"placeholder": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"originalWidth": 128,
|
||||
"originalHeight": 128
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/api/editor/images/pixel-art-snaps")
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(request_body.to_string()))
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn editor_pixel_art_snap_rejects_inline_source_before_processing() {
|
||||
let state = AppState::new(AppConfig {
|
||||
external_generation_mode: ExternalGenerationMode::Queue,
|
||||
..AppConfig::default()
|
||||
})
|
||||
.expect("state should build");
|
||||
let seed_user = seed_phone_user_with_password(&state, "13800138230", TEST_PASSWORD).await;
|
||||
let token = sign_test_user_token(&state, &seed_user, "sess_editor_pixel_snap_body");
|
||||
let app = build_router(state);
|
||||
let request_body = serde_json::json!({
|
||||
"sourceImageSrc": "data:image/png;base64,AAAA",
|
||||
"projectId": "proj-source",
|
||||
"sourceResourceId": "editor-resource-source",
|
||||
"assetKind": "character",
|
||||
"generationInputs": { "fields": [], "references": [] },
|
||||
"canvasCompletion": {
|
||||
"dialogId": "dialog-pixel-art",
|
||||
"title": "完美像素",
|
||||
"placeholder": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"originalWidth": 128,
|
||||
"originalHeight": 128
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/api/editor/images/pixel-art-snaps")
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(request_body.to_string()))
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||
let body = response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.expect("response body should collect")
|
||||
.to_bytes();
|
||||
let body_text = String::from_utf8_lossy(&body);
|
||||
assert!(
|
||||
body_text.contains("先上传 OSS"),
|
||||
"handler should reject inline pixel-art sources: {body_text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn editor_pixel_art_snap_requires_dialog_id_before_project_or_media_work() {
|
||||
let state = AppState::new(AppConfig::default()).expect("state should build");
|
||||
let seed_user = seed_phone_user_with_password(&state, "13800138231", TEST_PASSWORD).await;
|
||||
let token = sign_test_user_token(&state, &seed_user, "sess_editor_pixel_snap_dialog");
|
||||
let app = build_router(state);
|
||||
let request_body = serde_json::json!({
|
||||
"sourceImageSrc": "editor-resource-source",
|
||||
"projectId": "proj-does-not-exist",
|
||||
"canvasCompletion": {
|
||||
"title": "完美像素",
|
||||
"placeholder": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"originalWidth": 128,
|
||||
"originalHeight": 128
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/api/editor/images/pixel-art-snaps")
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(request_body.to_string()))
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||
let body = response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.expect("response body should collect")
|
||||
.to_bytes();
|
||||
let body_text = String::from_utf8_lossy(&body);
|
||||
assert!(
|
||||
body_text.contains("canvasCompletion.dialogId"),
|
||||
"handler should reject missing dialog ids before project lookup: {body_text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn editor_generation_json_validation_preserves_unsupported_media_type() {
|
||||
let state = AppState::new(AppConfig {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,9 +22,9 @@ use crate::{
|
||||
get_editor_asset_library, get_editor_generation_pricing, get_editor_project,
|
||||
list_editor_projects, list_public_editor_project_resources, load_recent_editor_project,
|
||||
remove_editor_image_background, rename_editor_project, save_editor_project_layout,
|
||||
split_editor_icon_spritesheet, submit_editor_asset_showcase,
|
||||
toggle_editor_showcase_asset_like, update_editor_asset, update_editor_asset_folder,
|
||||
update_editor_project_resource_showcase,
|
||||
snap_editor_image_to_pixel_art, split_editor_icon_spritesheet,
|
||||
submit_editor_asset_showcase, toggle_editor_showcase_asset_like, update_editor_asset,
|
||||
update_editor_asset_folder, update_editor_project_resource_showcase,
|
||||
},
|
||||
state::AppState,
|
||||
};
|
||||
@@ -205,6 +205,13 @@ pub fn router(state: AppState) -> Router<AppState> {
|
||||
require_bearer_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/api/editor/images/pixel-art-snaps",
|
||||
post(snap_editor_image_to_pixel_art).route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_bearer_auth,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/api/editor/icon-spritesheets/generations",
|
||||
post(generate_editor_icon_spritesheet).route_layer(middleware::from_fn_with_state(
|
||||
|
||||
@@ -5,7 +5,8 @@ pub mod vector_engine;
|
||||
|
||||
pub use pixel_art_snapper::{
|
||||
PIXEL_ART_ALPHA_COVERAGE_THRESHOLD, PIXEL_ART_ANALYSIS_COLORS, PIXEL_ART_KMEANS_SAMPLE_LIMIT,
|
||||
PIXEL_ART_MAX_IMAGE_PIXELS, PixelArtSnapError, snap_pixel_art, snap_pixel_art_with_deadline,
|
||||
PIXEL_ART_MAX_IMAGE_PIXELS, PixelArtSnapError, snap_pixel_art, snap_pixel_art_strict,
|
||||
snap_pixel_art_strict_with_deadline, snap_pixel_art_with_deadline,
|
||||
};
|
||||
pub use vector_engine::{
|
||||
DownloadedImage, GPT_IMAGE_2_C_MODEL, GPT_IMAGE_2_MODEL, GeneratedImages, NANOBANANA_2_MODEL,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user