fix(site): 精简站点配置,移除名称/Banner/相关链接 (#15)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-11-10 17:55:47 +08:00
1 parent 608afb88ea
commit 460cfdc78a
8 files changed
+24 -238

No files matched your search

+1 -42
View File
@@ -1,5 +1,3 @@
import json
from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Request, UploadFile, status
from pydantic import BaseModel
from sqlalchemy import func, select
@@ -28,7 +26,7 @@ from app.services.storage import (
router = APIRouter(dependencies=[Depends(require_staff)])
_TEXT_KEYS = {"site_name", "icp_text", "police_text", "link_cirea", "link_housing"}
_TEXT_KEYS = {"icp_text", "police_text", "link_cirea", "link_housing"}
_LINK_KEYS = {"link_cirea", "link_housing"}
_POSITIVE = {
"upload_max_bytes": "上传大小上限必须是正整数",
@@ -97,41 +95,6 @@ def _logo_id(value) -> str:
return str(int(text))
def _banner_ids(value) -> str:
if not isinstance(value, list):
_bad("banner_file_ids 必须是数组")
numbers: list[int] = []
for item in value:
if isinstance(item, bool) or not isinstance(item, (int, str)):
_bad("banner_file_ids 必须是附件编号")
text = str(item).strip()
if not text.isdigit() or int(text) <= 0:
_bad("banner_file_ids 必须是附件编号")
numbers.append(int(text))
return json.dumps(numbers, separators=(",", ":"))
def _friend_links(value) -> str:
if not isinstance(value, list):
_bad("friend_links 必须是数组")
cleaned: list[dict[str, str]] = []
for item in value:
if not isinstance(item, dict):
_bad("相关链接必须包含名称和地址")
name = item.get("name")
url = item.get("url")
if not isinstance(name, str) or not isinstance(url, str):
_bad("相关链接必须包含名称和地址")
name = name.strip()
url = url.strip()
if not name or not url:
_bad("相关链接必须包含名称和地址")
if is_javascript_url(url):
_bad("链接地址不正确")
cleaned.append({"name": name, "url": url})
return json.dumps(cleaned, ensure_ascii=False, separators=(",", ":"))
def _positive(value, message: str) -> str:
if isinstance(value, bool) or not isinstance(value, int) or value <= 0 or value > _INT_MAX:
_bad(message)
@@ -155,10 +118,6 @@ def _normalize_settings(payload: dict) -> dict[str, str]:
stored[key] = _text_setting(key, value)
elif key == "logo_file_id":
stored[key] = _logo_id(value)
elif key == "banner_file_ids":
stored[key] = _banner_ids(value)
elif key == "friend_links":
stored[key] = _friend_links(value)
elif key in _POSITIVE:
stored[key] = _positive(value, _POSITIVE[key])
elif key in _WORKDAYS:
-3
View File
@@ -12,12 +12,9 @@ def read_public_settings(db: Session = Depends(get_db)) -> dict:
# 只返回门户展示字段。上传上限和登录锁定参数留在管理端。
data = settings_view(db)
return {
"siteName": data["site_name"],
"logoUrl": data["logoUrl"],
"bannerUrls": data["bannerUrls"],
"icpText": data["icp_text"],
"policeText": data["police_text"],
"friendLinks": data["friend_links"],
"linkCirea": data["link_cirea"],
"linkHousing": data["link_housing"],
}
-15
View File
@@ -1,4 +1,3 @@
import json
from datetime import datetime, timezone
from sqlalchemy import DateTime, Integer, String, Text, select
@@ -11,12 +10,9 @@ def _mysql_args() -> dict:
return {"mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}
DEFAULTS: dict[str, str] = {
"site_name": "甘肃省房地产价格评估行业协会",
"logo_file_id": "",
"banner_file_ids": "[]",
"icp_text": "陇ICP备19001227号",
"police_text": "甘公网安备62010202001984号",
"friend_links": "[]",
"link_cirea": "",
"link_housing": "",
"complaint_workdays": "",
@@ -27,7 +23,6 @@ DEFAULTS: dict[str, str] = {
}
SETTING_KEYS = tuple(DEFAULTS)
_JSON_KEYS = {"banner_file_ids", "friend_links"}
_NUMBER_KEYS = {"upload_max_bytes", "login_max_failures", "login_lock_minutes"}
_OPTIONAL_NUMBER_KEYS = {"complaint_workdays", "expert_reply_workdays"}
@@ -109,14 +104,6 @@ def save_settings(db, values: dict[str, str]) -> None:
def _present(key: str, raw: str):
if key in _JSON_KEYS:
if not raw:
return []
try:
parsed = json.loads(raw)
except json.JSONDecodeError:
return []
return parsed if isinstance(parsed, list) else []
if key in _NUMBER_KEYS:
text = str(raw).strip()
if text.isdigit():
@@ -160,6 +147,4 @@ def settings_view(db) -> dict:
logo_id = raw.get("logo_file_id", "")
logo_urls = _public_urls(db, [logo_id]) if str(logo_id).strip() else []
view["logoUrl"] = logo_urls[0] if logo_urls else ""
banners = view["banner_file_ids"] if isinstance(view["banner_file_ids"], list) else []
view["bannerUrls"] = _public_urls(db, banners)
return view
+4 -3
View File
@@ -301,7 +301,6 @@ def main() -> None:
"/api/admin/site-settings",
headers=headers,
json={
"site_name": "验收站点",
"icp_text": "陇ICP备19001227号",
"link_cirea": "https://www.cirea.org.cn/",
},
@@ -310,10 +309,12 @@ def main() -> None:
fail(f"site {site.status_code} {site.text[:200]}")
public_site = client.get("/api/site/settings")
public_body = public_site.json()
if public_body.get("siteName") != "验收站点" or public_body.get("linkCirea") != "https://www.cirea.org.cn/":
if public_body.get("icpText") != "陇ICP备19001227号" or public_body.get("linkCirea") != "https://www.cirea.org.cn/":
fail(f"public site {public_body}")
if "loginMaxFailures" in public_body or "login_max_failures" in public_body:
fail("public site leaked lock settings")
if "siteName" in public_body or "bannerUrls" in public_body or "friendLinks" in public_body:
fail(f"public site kept removed keys {public_body}")
probe_word = f"验收敏感词{uuid.uuid4().hex[:6]}"
word = client.post("/api/admin/sensitive-words", headers=headers, json={"word": probe_word})
@@ -340,7 +341,7 @@ def main() -> None:
client.put(
"/api/admin/site-settings",
headers=headers,
json={"site_name": "甘肃省房地产价格评估行业协会", "link_cirea": ""},
json={"link_cirea": ""},
)
db = SessionLocal()
+13 -13
View File
@@ -13,12 +13,9 @@ from app.core.config import get_settings
from app.services.html_filter import filter_html
_PUBLIC_KEYS = {
"siteName",
"logoUrl",
"bannerUrls",
"icpText",
"policeText",
"friendLinks",
"linkCirea",
"linkHousing",
}
@@ -224,7 +221,7 @@ def test_admin_content_requires_login(api_client):
("get", "/api/admin/files/1", None),
("get", "/api/admin/site-settings", None),
("get", "/api/admin/sensitive-words", None),
("put", "/api/admin/site-settings", {"site_name": "x"}),
("put", "/api/admin/site-settings", {"icp_text": "x"}),
("post", "/api/admin/sensitive-words", {"word": "x"}),
("post", "/api/admin/content/preview-filter", {"html": "<p>a</p>"}),
("delete", "/api/admin/sensitive-words/1", None),
@@ -309,8 +306,6 @@ def test_public_settings_hide_login_lock(ready_client):
assert isinstance(original["login_max_failures"], int)
assert isinstance(original["login_lock_minutes"], int)
assert isinstance(original["logoUrl"], str)
assert isinstance(original["bannerUrls"], list)
link = {"name": "示例链接", "url": "https://example.com/gsrea"}
try:
rejected = ready_client.put(
"/api/admin/site-settings",
@@ -318,12 +313,17 @@ def test_public_settings_hide_login_lock(ready_client):
headers=headers,
)
assert rejected.status_code == 400
unknown = ready_client.put(
"/api/admin/site-settings",
json={"site_name": "甘肃价协测试站点"},
headers=headers,
)
assert unknown.status_code == 400
saved = ready_client.put(
"/api/admin/site-settings",
json={
"site_name": "甘肃价协测试站点",
"icp_text": "陇ICP备测试号",
"friend_links": [link],
"link_cirea": "https://example.com/cirea",
},
headers=headers,
)
@@ -332,11 +332,9 @@ def test_public_settings_hide_login_lock(ready_client):
assert response.status_code == 200, response.text
body = response.json()
assert set(body) == _PUBLIC_KEYS
assert body["siteName"] == "甘肃价协测试站点"
assert body["icpText"] == "陇ICP备测试号"
assert body["friendLinks"] == [link]
assert body["linkCirea"] == "https://example.com/cirea"
assert isinstance(body["logoUrl"], str)
assert isinstance(body["bannerUrls"], list)
text = response.text
assert "login_max_failures" not in text
assert "login_lock_minutes" not in text
@@ -344,13 +342,15 @@ def test_public_settings_hide_login_lock(ready_client):
assert "loginLockMinutes" not in text
assert "upload_max_bytes" not in text
assert "uploadMaxBytes" not in text
assert "siteName" not in text
assert "bannerUrls" not in text
assert "friendLinks" not in text
finally:
ready_client.put(
"/api/admin/site-settings",
json={
"site_name": original["site_name"],
"icp_text": original["icp_text"],
"friend_links": original["friend_links"],
"link_cirea": original["link_cirea"],
},
headers=headers,
)