热修图片画布布局上限至2MiB
将 API 与 SpacetimeDB 的画布布局校验上限统一提高到 2 MiB 为站内与外部画布保存接口增加 4 MiB 请求体上限 补充 2 MiB 边界通过和超限拒绝测试
This commit is contained in:
@@ -96,7 +96,8 @@ pub(crate) const EDITOR_PROJECT_ID_PREFIX: &str = "proj-";
|
||||
pub(crate) const EDITOR_RESOURCE_ID_PREFIX: &str = "editor-resource-";
|
||||
pub(crate) const EDITOR_ASSET_FOLDER_ID_PREFIX: &str = "editor-asset-folder-";
|
||||
pub(crate) const EDITOR_ASSET_ID_PREFIX: &str = "editor-asset-";
|
||||
const EDITOR_LAYOUT_MAX_BYTES: usize = 256 * 1024;
|
||||
const EDITOR_LAYOUT_MAX_BYTES: usize = 2 * 1024 * 1024;
|
||||
pub(crate) const EDITOR_LAYOUT_REQUEST_BODY_MAX_BYTES: usize = 4 * 1024 * 1024;
|
||||
pub(crate) const EDITOR_PROJECT_DEFAULT_TITLE: &str = "未命名画布";
|
||||
const EDITOR_ASSET_METADATA_MAX_BYTES: usize = 64 * 1024;
|
||||
const EDITOR_ASSET_DEFAULT_FOLDER_ID: &str = "project";
|
||||
@@ -8750,6 +8751,24 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_layout_serialization_accepts_payload_up_to_two_mib() {
|
||||
let payload = json!("x".repeat(EDITOR_LAYOUT_MAX_BYTES - 2));
|
||||
|
||||
let serialized = serialize_editor_layers(payload).expect("two MiB layout should save");
|
||||
|
||||
assert_eq!(serialized.len(), EDITOR_LAYOUT_MAX_BYTES);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_layout_serialization_rejects_payload_over_two_mib() {
|
||||
let payload = json!("x".repeat(EDITOR_LAYOUT_MAX_BYTES - 1));
|
||||
|
||||
let error = serialize_editor_layers(payload).expect_err("oversized layout should fail");
|
||||
|
||||
assert!(error.body_text().contains("图层布局过大"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_asset_metadata_serialization_rejects_nested_inline_media() {
|
||||
let error = serialize_editor_asset_metadata(Some(json!({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use axum::{
|
||||
Router,
|
||||
extract::DefaultBodyLimit,
|
||||
handler::Handler,
|
||||
middleware,
|
||||
routing::{get, patch, post},
|
||||
};
|
||||
@@ -14,12 +15,12 @@ use crate::{
|
||||
get_editor_agent_conversation, list_editor_agent_conversations,
|
||||
},
|
||||
editor_project::{
|
||||
create_editor_asset, create_editor_asset_folder, create_editor_project,
|
||||
create_editor_project_resource, delete_editor_asset, delete_editor_asset_folder,
|
||||
delete_editor_project, edit_editor_image, extract_editor_ui_design_assets,
|
||||
generate_editor_icon_spritesheet, generate_editor_image, get_editor_asset_library,
|
||||
get_editor_generation_pricing, get_editor_project, list_editor_projects,
|
||||
list_public_editor_project_resources, load_recent_editor_project,
|
||||
EDITOR_LAYOUT_REQUEST_BODY_MAX_BYTES, create_editor_asset, create_editor_asset_folder,
|
||||
create_editor_project, create_editor_project_resource, delete_editor_asset,
|
||||
delete_editor_asset_folder, delete_editor_project, edit_editor_image,
|
||||
extract_editor_ui_design_assets, generate_editor_icon_spritesheet, generate_editor_image,
|
||||
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,
|
||||
@@ -59,7 +60,10 @@ pub fn router(state: AppState) -> Router<AppState> {
|
||||
.route(
|
||||
"/api/editor/projects/{project_id}",
|
||||
get(get_editor_project)
|
||||
.patch(save_editor_project_layout)
|
||||
.patch(
|
||||
save_editor_project_layout
|
||||
.layer(DefaultBodyLimit::max(EDITOR_LAYOUT_REQUEST_BODY_MAX_BYTES)),
|
||||
)
|
||||
.delete(delete_editor_project)
|
||||
.route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use axum::{
|
||||
Router, middleware,
|
||||
Router,
|
||||
extract::DefaultBodyLimit,
|
||||
middleware,
|
||||
routing::{get, patch, post},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
editor_project::EDITOR_LAYOUT_REQUEST_BODY_MAX_BYTES,
|
||||
external_api_auth::require_external_api_key,
|
||||
external_assets_api::{
|
||||
confirm_external_asset_object, create_external_direct_upload_ticket,
|
||||
@@ -84,10 +87,12 @@ pub fn router(state: AppState) -> Router<AppState> {
|
||||
)
|
||||
.route(
|
||||
"/api/external/v1/editor/projects/{project_id}/canvas",
|
||||
patch(save_external_editor_canvas).route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_external_api_key,
|
||||
)),
|
||||
patch(save_external_editor_canvas)
|
||||
.layer(DefaultBodyLimit::max(EDITOR_LAYOUT_REQUEST_BODY_MAX_BYTES))
|
||||
.route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
require_external_api_key,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/api/external/v1/editor/projects/{project_id}/resources",
|
||||
|
||||
@@ -7,7 +7,7 @@ const EDITOR_ASSET_DEFAULT_FOLDER_ID: &str = "project";
|
||||
const EDITOR_ASSET_DEFAULT_FOLDER_LABEL: &str = "项目素材";
|
||||
const EDITOR_PROJECT_MAX_TITLE_CHARS: usize = 80;
|
||||
const EDITOR_ASSET_MAX_LABEL_CHARS: usize = 80;
|
||||
const EDITOR_PROJECT_MAX_LAYOUT_JSON_BYTES: usize = 256 * 1024;
|
||||
const EDITOR_PROJECT_MAX_LAYOUT_JSON_BYTES: usize = 2 * 1024 * 1024;
|
||||
const EDITOR_PROJECT_SOURCE_TYPES: [&str; 3] = ["uploaded", "generated", "mock_generated"];
|
||||
const EDITOR_SHOWCASE_STATUS_PENDING: &str = "pending";
|
||||
const EDITOR_SHOWCASE_STATUS_APPROVED: &str = "approved";
|
||||
@@ -4997,6 +4997,30 @@ mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn editor_layout_accepts_json_up_to_two_mib() {
|
||||
let payload = format!(
|
||||
"\"{}\"",
|
||||
"x".repeat(EDITOR_PROJECT_MAX_LAYOUT_JSON_BYTES - 2)
|
||||
);
|
||||
|
||||
let normalized = normalize_layout_json(payload).expect("two MiB layout should save");
|
||||
|
||||
assert_eq!(normalized.len(), EDITOR_PROJECT_MAX_LAYOUT_JSON_BYTES);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_layout_rejects_json_over_two_mib() {
|
||||
let payload = format!(
|
||||
"\"{}\"",
|
||||
"x".repeat(EDITOR_PROJECT_MAX_LAYOUT_JSON_BYTES - 1)
|
||||
);
|
||||
|
||||
let error = normalize_layout_json(payload).expect_err("oversized layout should fail");
|
||||
|
||||
assert_eq!(error, "图片画布图层布局过大");
|
||||
}
|
||||
|
||||
fn public_showcase_asset() -> EditorShowcaseAsset {
|
||||
let now = Timestamp::from_micros_since_unix_epoch(1_000_000);
|
||||
EditorShowcaseAsset {
|
||||
|
||||
Reference in New Issue
Block a user