@@ -132,4 +132,4 @@ BEATMAP_PROXY_PREFIX=b-ppy
|
|||||||
SAVE_REPLAYS=0
|
SAVE_REPLAYS=0
|
||||||
REDIS_HOST=localhost
|
REDIS_HOST=localhost
|
||||||
SHARED_INTEROP_DOMAIN=http://localhost:8000
|
SHARED_INTEROP_DOMAIN=http://localhost:8000
|
||||||
SERVER_PORT=8006
|
SERVER_PORT=80
|
||||||
|
|||||||
@@ -61,11 +61,11 @@ class Event(UTCBaseModel, SQLModel, table=True):
|
|||||||
# 临时修复:统一成就事件格式 (TODO: 可在数据迁移完成后移除)
|
# 临时修复:统一成就事件格式 (TODO: 可在数据迁移完成后移除)
|
||||||
if self.type == EventType.ACHIEVEMENT and "achievement" in self.event_payload:
|
if self.type == EventType.ACHIEVEMENT and "achievement" in self.event_payload:
|
||||||
achievement_data = self.event_payload["achievement"]
|
achievement_data = self.event_payload["achievement"]
|
||||||
if (
|
if "achievement_id" in achievement_data and (
|
||||||
"achievement_id" in achievement_data
|
"name" not in achievement_data or "slug" not in achievement_data
|
||||||
and ("name" not in achievement_data or "slug" not in achievement_data)
|
|
||||||
):
|
):
|
||||||
from app.models.achievement import MEDALS
|
from app.models.achievement import MEDALS
|
||||||
|
|
||||||
achievement_id = achievement_data["achievement_id"]
|
achievement_id = achievement_data["achievement_id"]
|
||||||
for medal in MEDALS:
|
for medal in MEDALS:
|
||||||
if medal.id == achievement_id:
|
if medal.id == achievement_id:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
class UserpageError(Exception):
|
class UserpageError(Exception):
|
||||||
"""用户页面处理错误基类"""
|
"""用户页面处理错误基类"""
|
||||||
|
|
||||||
def __init__(self, message: str, code: str = "userpage_error"):
|
def __init__(self, message: str, code: str = "userpage_error"):
|
||||||
self.message = message
|
self.message = message
|
||||||
self.code = code
|
self.code = code
|
||||||
@@ -15,6 +16,7 @@ class UserpageError(Exception):
|
|||||||
|
|
||||||
class ContentTooLongError(UserpageError):
|
class ContentTooLongError(UserpageError):
|
||||||
"""内容过长错误"""
|
"""内容过长错误"""
|
||||||
|
|
||||||
def __init__(self, current_length: int, max_length: int):
|
def __init__(self, current_length: int, max_length: int):
|
||||||
message = f"Content too long. Maximum {max_length} characters allowed, got {current_length}."
|
message = f"Content too long. Maximum {max_length} characters allowed, got {current_length}."
|
||||||
super().__init__(message, "content_too_long")
|
super().__init__(message, "content_too_long")
|
||||||
@@ -24,12 +26,14 @@ class ContentTooLongError(UserpageError):
|
|||||||
|
|
||||||
class ContentEmptyError(UserpageError):
|
class ContentEmptyError(UserpageError):
|
||||||
"""内容为空错误"""
|
"""内容为空错误"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("Content cannot be empty.", "content_empty")
|
super().__init__("Content cannot be empty.", "content_empty")
|
||||||
|
|
||||||
|
|
||||||
class BBCodeValidationError(UserpageError):
|
class BBCodeValidationError(UserpageError):
|
||||||
"""BBCode验证错误"""
|
"""BBCode验证错误"""
|
||||||
|
|
||||||
def __init__(self, errors: list[str]):
|
def __init__(self, errors: list[str]):
|
||||||
message = f"BBCode validation failed: {'; '.join(errors)}"
|
message = f"BBCode validation failed: {'; '.join(errors)}"
|
||||||
super().__init__(message, "bbcode_validation_error")
|
super().__init__(message, "bbcode_validation_error")
|
||||||
@@ -38,6 +42,7 @@ class BBCodeValidationError(UserpageError):
|
|||||||
|
|
||||||
class ForbiddenTagError(UserpageError):
|
class ForbiddenTagError(UserpageError):
|
||||||
"""禁止标签错误"""
|
"""禁止标签错误"""
|
||||||
|
|
||||||
def __init__(self, tag: str):
|
def __init__(self, tag: str):
|
||||||
message = f"Forbidden tag '{tag}' is not allowed."
|
message = f"Forbidden tag '{tag}' is not allowed."
|
||||||
super().__init__(message, "forbidden_tag")
|
super().__init__(message, "forbidden_tag")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class UpdateUserpageRequest(BaseModel):
|
|||||||
body: str = Field(
|
body: str = Field(
|
||||||
description="用户页面的BBCode原始内容",
|
description="用户页面的BBCode原始内容",
|
||||||
max_length=60000,
|
max_length=60000,
|
||||||
examples=["[b]Hello![/b] This is my profile page.\n[color=blue]Blue text[/color]"]
|
examples=["[b]Hello![/b] This is my profile page.\n[color=blue]Blue text[/color]"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@field_validator("body")
|
@field_validator("body")
|
||||||
@@ -47,10 +47,7 @@ class UserpageResponse(BaseModel):
|
|||||||
class ValidateBBCodeRequest(BaseModel):
|
class ValidateBBCodeRequest(BaseModel):
|
||||||
"""验证BBCode请求模型"""
|
"""验证BBCode请求模型"""
|
||||||
|
|
||||||
content: str = Field(
|
content: str = Field(description="要验证的BBCode内容", max_length=60000)
|
||||||
description="要验证的BBCode内容",
|
|
||||||
max_length=60000
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ValidateBBCodeResponse(BaseModel):
|
class ValidateBBCodeResponse(BaseModel):
|
||||||
|
|||||||
@@ -25,12 +25,34 @@ class BBCodeService:
|
|||||||
|
|
||||||
# 允许的HTML标签和属性 - 基于官方实现
|
# 允许的HTML标签和属性 - 基于官方实现
|
||||||
ALLOWED_TAGS: ClassVar[list[str]] = [
|
ALLOWED_TAGS: ClassVar[list[str]] = [
|
||||||
"a", "audio", "blockquote", "br", "button", "center", "code", "del", "div", "em", "h2", "h4",
|
"a",
|
||||||
"iframe", "img", "li", "ol", "p", "pre", "span", "strong", "u", "ul",
|
"audio",
|
||||||
|
"blockquote",
|
||||||
|
"br",
|
||||||
|
"button",
|
||||||
|
"center",
|
||||||
|
"code",
|
||||||
|
"del",
|
||||||
|
"div",
|
||||||
|
"em",
|
||||||
|
"h2",
|
||||||
|
"h4",
|
||||||
|
"iframe",
|
||||||
|
"img",
|
||||||
|
"li",
|
||||||
|
"ol",
|
||||||
|
"p",
|
||||||
|
"pre",
|
||||||
|
"span",
|
||||||
|
"strong",
|
||||||
|
"u",
|
||||||
|
"ul",
|
||||||
# imagemap 相关
|
# imagemap 相关
|
||||||
"map", "area",
|
"map",
|
||||||
|
"area",
|
||||||
# 自定义容器
|
# 自定义容器
|
||||||
"details", "summary",
|
"details",
|
||||||
|
"summary",
|
||||||
]
|
]
|
||||||
|
|
||||||
ALLOWED_ATTRIBUTES: ClassVar[dict[str, list[str]]] = {
|
ALLOWED_ATTRIBUTES: ClassVar[dict[str, list[str]]] = {
|
||||||
@@ -57,8 +79,22 @@ class BBCodeService:
|
|||||||
|
|
||||||
# 危险的BBCode标签(不允许)
|
# 危险的BBCode标签(不允许)
|
||||||
FORBIDDEN_TAGS: ClassVar[list[str]] = [
|
FORBIDDEN_TAGS: ClassVar[list[str]] = [
|
||||||
"script", "iframe", "object", "embed", "form", "input", "textarea",
|
"script",
|
||||||
"select", "option", "meta", "link", "style", "title", "head", "html", "body",
|
"iframe",
|
||||||
|
"object",
|
||||||
|
"embed",
|
||||||
|
"form",
|
||||||
|
"input",
|
||||||
|
"textarea",
|
||||||
|
"select",
|
||||||
|
"option",
|
||||||
|
"meta",
|
||||||
|
"link",
|
||||||
|
"style",
|
||||||
|
"title",
|
||||||
|
"head",
|
||||||
|
"html",
|
||||||
|
"body",
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -256,14 +292,11 @@ class BBCodeService:
|
|||||||
|
|
||||||
if href == "#":
|
if href == "#":
|
||||||
# 无链接区域
|
# 无链接区域
|
||||||
links.append(
|
links.append(f'<span class="imagemap__link" style="{style}" title="{title}"></span>')
|
||||||
f'<span class="imagemap__link" style="{style}" title="{title}"></span>'
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# 有链接区域
|
# 有链接区域
|
||||||
links.append(
|
links.append(
|
||||||
f'<a class="imagemap__link" href="{href}" style="{style}" '
|
f'<a class="imagemap__link" href="{href}" style="{style}" title="{title}"></a>'
|
||||||
f'title="{title}"></a>'
|
|
||||||
)
|
)
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
continue
|
continue
|
||||||
@@ -340,8 +373,7 @@ class BBCodeService:
|
|||||||
"""解析 [quote] 标签"""
|
"""解析 [quote] 标签"""
|
||||||
# [quote="author"]content[/quote]
|
# [quote="author"]content[/quote]
|
||||||
pattern1 = r'\[quote="([^"]+)"\]\s*(.*?)\s*\[/quote\]'
|
pattern1 = r'\[quote="([^"]+)"\]\s*(.*?)\s*\[/quote\]'
|
||||||
text = re.sub(pattern1, r"<blockquote><h4>\1 wrote:</h4>\2</blockquote>", text,
|
text = re.sub(pattern1, r"<blockquote><h4>\1 wrote:</h4>\2</blockquote>", text, flags=re.DOTALL | re.IGNORECASE)
|
||||||
flags=re.DOTALL | re.IGNORECASE)
|
|
||||||
|
|
||||||
# [quote]content[/quote]
|
# [quote]content[/quote]
|
||||||
pattern2 = r"\[quote\]\s*(.*?)\s*\[/quote\]"
|
pattern2 = r"\[quote\]\s*(.*?)\s*\[/quote\]"
|
||||||
|
|||||||
Reference in New Issue
Block a user