feat(plugins): 4 个业务插件代码 + 前台公开页面改造

plugins/navManager:导航 CRUD + 导航分类 CRUD(独立 blueprint)
plugins/friendManager:友情链接 CRUD
plugins/aboutManager:关于本站 CRUD
plugins/siteStats:访问统计主页 + JSON 接口 + /site/* GET 埋点 + nav 卡片点击埋点

applications/view/public/nav.py:导航聚合页 / 分类详情 / JSON 接口,支持 visit_count 注入
applications/view/public/about.py:关于本站公开页
applications/view/public/friend.py:友情链接公开页

templates/public/base.html:响应式首页(搜索框 / 分类抽屉 / 5 主题切换 / footer 隐私小字)
templates/public/index.html / category.html:卡片右下角访问次数
templates/public/about.html / friend.html:关于 + 友链前台页

docs/plugins-development.md:插件开发完整指南(生命周期 + 4 个示例 + FAQ + framework 迁移)

scripts/:探针与端到端验证脚本(probe_*/verify_*/test_*)
This commit is contained in:
bwstudio
2026-09-06 18:18:06 +08:00
parent 1ea69c2c34
commit 0ffc103e46
53 changed files with 4705 additions and 89 deletions
+65
View File
@@ -0,0 +1,65 @@
"""
「关于本站」后台管理:编辑单条记录 (id=1)。
插件版。
"""
import os
from flask import Blueprint, render_template, request
from flask_wtf.csrf import validate_csrf
from wtforms.validators import ValidationError
from applications.common.utils.http import fail_api, success_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import str_escape
from applications.extensions import db
from applications.models import About
dir_path = os.path.dirname(os.path.abspath(__file__))
bp = Blueprint(
'about', __name__,
url_prefix='/system/about',
template_folder=os.path.join(dir_path, '..', 'templates'),
)
def _get_about() -> About:
a = About.query.get(1)
if a is None:
a = About(id=1, site_name='旺珂 · 导航')
db.session.add(a)
db.session.commit()
return a
@bp.get('/')
@authorize("site:about:main")
def main():
about = _get_about()
return render_template('system/about/main.html', about=about)
@bp.post('/save')
@authorize("site:about:main", log=True)
def save():
req_json = request.get_json(force=True, silent=True) or {}
try:
validate_csrf(req_json.get("csrf_token"))
except ValidationError:
return fail_api(msg='非法请求')
a = _get_about()
fields = {
'site_name': (req_json.get('siteName') or '').strip()[:120],
'content_md': (req_json.get('contentMd') or '').strip(),
'icp': (req_json.get('icp') or '').strip()[:120],
'contact_email': (req_json.get('contactEmail') or '').strip()[:120],
'contact_qq': (req_json.get('contactQq') or '').strip()[:32],
'contact_wechat': (req_json.get('contactWechat') or '').strip()[:120],
'contact_telegram': (req_json.get('contactTelegram') or '').strip()[:120],
'contact_github': (req_json.get('contactGithub') or '').strip()[:255],
'donate_url': (req_json.get('donateUrl') or '').strip()[:255],
}
for k, v in fields.items():
setattr(a, k, v)
db.session.commit()
return success_api(msg='保存成功')