Fix API compatibility and add user/role/permission and asset import/export

This commit is contained in:
2026-01-25 23:36:23 +08:00
commit 501d11e14e
371 changed files with 68853 additions and 0 deletions

73
backend/app/utils/case.py Normal file
View File

@@ -0,0 +1,73 @@
"""
Case conversion utilities for API payloads.
"""
import re
from typing import Any, Dict, List
_FIRST_CAP_RE = re.compile(r"(.)([A-Z][a-z]+)")
_ALL_CAP_RE = re.compile(r"([a-z0-9])([A-Z])")
_SPECIAL_ALIASES = {
"avatar_url": "avatar",
"avatarUrl": "avatar",
}
def to_snake(name: str) -> str:
"""Convert camelCase/PascalCase to snake_case."""
if not isinstance(name, str) or not name:
return name
if name.lower() == name:
return name
s1 = _FIRST_CAP_RE.sub(r"\1_\2", name)
s2 = _ALL_CAP_RE.sub(r"\1_\2", s1)
return s2.lower()
def to_camel(name: str) -> str:
"""Convert snake_case to camelCase."""
if not isinstance(name, str) or not name:
return name
if "_" not in name:
return name
parts = name.split("_")
if not parts:
return name
first = parts[0]
rest = "".join(part.capitalize() if part else "_" for part in parts[1:])
return first + rest
def convert_keys_to_snake(data: Any) -> Any:
"""Recursively convert dict keys to snake_case."""
if isinstance(data, list):
return [convert_keys_to_snake(item) for item in data]
if isinstance(data, dict):
converted: Dict[Any, Any] = {}
for key, value in data.items():
new_key = to_snake(key) if isinstance(key, str) else key
converted[new_key] = convert_keys_to_snake(value)
return converted
return data
def add_camelcase_aliases(data: Any) -> Any:
"""Recursively add camelCase aliases for snake_case keys."""
if isinstance(data, list):
return [add_camelcase_aliases(item) for item in data]
if isinstance(data, dict):
result: Dict[Any, Any] = {}
for key, value in data.items():
result[key] = add_camelcase_aliases(value)
# Add camelCase aliases without overriding existing keys
for key, value in list(result.items()):
if isinstance(key, str):
camel_key = to_camel(key)
if camel_key != key and camel_key not in result:
result[camel_key] = value
alias_key = _SPECIAL_ALIASES.get(key)
if alias_key and alias_key not in result:
result[alias_key] = value
return result
return data