Files
pear-admin-flask/plugins/cookieManager/view/cookie_site.py
T
bwstudio 6f4af8664e feat(cookieManager): 登录用户去掉Cookie值/域名,登录网站增加关联用户下拉
登录用户:
- 模型删除 cookie_value / domain 两列(连同 to_dict)
- 列表表头、新增/编辑表单移除对应控件;保存/更新不再校验 Cookie 值
  (改为只校验 用户名 + 网站URL)

登录网站:
- CookieSite 新增 cookie_user_id(关联 admin_cookie_user.id,可空)
- 列表新增「关联用户」列,未关联显示灰色「未关联」标签;查询栏支持按用户筛选
- 新增/编辑页用 layui 下拉框选择登录用户(lay-search 可搜索,显示
  “昵称(用户名)· 网站名”),编辑时自动回显已关联项
- Schema 增加 cookie_username 输出字段,视图一次性查用户表做映射,避免逐行查库

迁移 e5f6a7b8c9d0:
- admin_cookie_user 删 cookie_value、domain(SQLite 用 batch 重建表)
- admin_cookie_site 加 cookie_user_id
- 每步先判断列是否存在,可重复执行;downgrade 可还原

注意:升级会丢弃 admin_cookie_user.cookie_value / domain 的历史数据。

已 Playwright 真机验证(本机 :5000):
列表无 Cookie值/域名列、新增页无对应控件、保存成功;
网站新增选中用户后落库 cookie_user_id=1、列表显示“验证用户”、编辑页正确回显。
2026-09-10 20:19:25 +08:00

216 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Cookie管理 - 登录网站视图
URL: /system/cookie-site
"""
import os
import re
from datetime import datetime
from flask import Blueprint, render_template, request
from flask_login import current_user
from applications.common import curd
from applications.common.utils.http import table_api, 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.extensions.init_limit import limiter
from applications.models import CookieSite, CookieUser
from applications.schemas import CookieSiteManageSchema
dir_path = os.path.dirname(os.path.abspath(__file__))
bp = Blueprint('cookie_site', __name__,
url_prefix='/system/cookie-site',
template_folder=os.path.join(dir_path, '..', 'templates'))
def _normalize_url(raw):
"""网址补齐协议头:用户常省略 http(s)://,此处自动补 https://。"""
if not raw or not str(raw).strip():
return raw
u = str(raw).strip()
if re.match(r'^[a-zA-Z][a-zA-Z0-9+.\-]*://', u):
return u
return 'https://' + u
def _parse_expiry(raw):
"""过期时间:空值转 None;非空字符串按常见格式解析为 datetime;解析失败返回 None。"""
if not raw or not str(raw).strip():
return None
raw = str(raw).strip()
for fmt in ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', '%Y-%m-%d'):
try:
return datetime.strptime(raw, fmt)
except ValueError:
continue
return None
def _user_options():
"""下拉框用的登录用户列表:id + 显示名(昵称优先,附用户名与网站)。"""
users = CookieUser.query.order_by(CookieUser.id.desc()).all()
opts = []
for u in users:
nick = (u.nickname or '').strip()
base = ('%s%s' % (nick, u.username)) if nick and nick != u.username else (nick or u.username)
site = (u.website_name or u.website_url or '').strip()
opts.append({'id': u.id, 'label': ('%s · %s' % (base, site)) if site else base})
return opts
def _set_cookie_username(items):
"""为每行补上关联登录用户的显示名,避免逐行查库。"""
ids = {i.cookie_user_id for i in items if i.cookie_user_id}
mapping = {}
if ids:
for u in CookieUser.query.filter(CookieUser.id.in_(ids)).all():
mapping[u.id] = u.nickname or u.username or ''
for i in items:
setattr(i, '_cookie_username', mapping.get(i.cookie_user_id, '') if i.cookie_user_id else '')
@bp.get('/')
@authorize("system:cookie-site:main")
def main():
return render_template('system/cookie_site/main.html', users=_user_options())
@bp.get('/data')
@limiter.limit("60 per minute")
@authorize("system:cookie-site:main")
def data():
name = str_escape(request.args.get('name', type=str))
domain = str_escape(request.args.get('domain', type=str))
cookie_user_id = request.args.get('cookie_user_id', type=int)
enable = request.args.get('enable', type=int)
query = CookieSite.query.filter()
if name:
query = query.filter(CookieSite.name.contains(name))
if domain:
query = query.filter(CookieSite.domain.contains(domain))
if cookie_user_id:
query = query.filter(CookieSite.cookie_user_id == cookie_user_id)
if enable is not None:
query = query.filter(CookieSite.enable == enable)
items = query.order_by(CookieSite.id.desc()).layui_paginate()
_set_cookie_username(items.items)
return table_api(
msg="请求成功",
data=curd.model_to_dicts(schema=CookieSiteManageSchema, data=items.items),
count=items.total,
)
@bp.get('/add')
@authorize("system:cookie-site:add", log=True)
def add():
return render_template('system/cookie_site/add.html', users=_user_options())
@bp.post('/save')
@authorize("system:cookie-site:add", log=True)
def save():
req_json = request.get_json(force=True, silent=True) or {}
name = str_escape(req_json.get('name'))
url = str_escape(req_json.get('url'))
cookie_value = str_escape(req_json.get('cookie_value'))
if not all([name, url, cookie_value]):
return fail_api(msg="名称/网址/Cookie值不得为空")
try:
obj = CookieSite(
cookie_user_id=int(req_json.get('cookie_user_id') or 0) or None,
name=name,
url=_normalize_url(url),
cookie_value=cookie_value,
domain=str_escape(req_json.get('domain')),
path=str_escape(req_json.get('path')) or '/',
expiry=_parse_expiry(req_json.get('expiry')),
secure=int(req_json.get('secure') or 0),
http_only=int(req_json.get('http_only') or 0),
enable=int(req_json.get('enable') or 1),
create_by=current_user.username if current_user.is_authenticated else 'admin',
)
db.session.add(obj)
db.session.commit()
except Exception as e:
db.session.rollback()
return fail_api(msg=f"新增失败:{e}")
return success_api(msg="新增成功")
@bp.get('/edit')
@authorize("system:cookie-site:edit", log=True)
def edit():
sid = request.args.get('cookieSiteId', type=int)
obj = curd.get_one_by_id(CookieSite, sid)
if not obj:
return fail_api(msg="记录不存在")
return render_template('system/cookie_site/edit.html', obj=obj, users=_user_options())
@bp.put('/update')
@authorize("system:cookie-site:edit", log=True)
def update():
req_json = request.get_json(force=True, silent=True) or {}
sid = req_json.get('cookieSiteId')
obj = curd.get_one_by_id(CookieSite, sid)
if not obj:
return fail_api(msg="记录不存在")
name = str_escape(req_json.get('name'))
url = str_escape(req_json.get('url'))
cookie_value = str_escape(req_json.get('cookie_value'))
if not all([name, url, cookie_value]):
return fail_api(msg="名称/网址/Cookie值不得为空")
try:
obj.cookie_user_id = int(req_json.get('cookie_user_id') or 0) or None
obj.name = name
obj.url = _normalize_url(url)
obj.cookie_value = cookie_value
obj.domain = str_escape(req_json.get('domain'))
obj.path = str_escape(req_json.get('path')) or '/'
obj.expiry = _parse_expiry(req_json.get('expiry'))
obj.secure = int(req_json.get('secure') or 0)
obj.http_only = int(req_json.get('http_only') or 0)
obj.enable = int(req_json.get('enable') or 1)
db.session.commit()
except Exception as e:
db.session.rollback()
return fail_api(msg=f"更新失败:{e}")
return success_api(msg="更新成功")
@bp.delete('/remove/<int:sid>')
@authorize("system:cookie-site:remove", log=True)
def remove(sid):
res = curd.delete_one_by_id(CookieSite, sid)
if not res:
return fail_api(msg="删除失败")
return success_api(msg="删除成功")
@bp.put('/enable')
@authorize("system:cookie-site:edit", log=True)
def enable():
req_json = request.get_json(force=True, silent=True) or {}
sid = req_json.get('cookieSiteId')
if not sid:
return fail_api(msg="数据错误")
res = curd.enable_status(CookieSite, sid)
if not res:
return fail_api(msg="操作失败")
return success_api(msg="已启用")
@bp.put('/disable')
@authorize("system:cookie-site:edit", log=True)
def disable():
req_json = request.get_json(force=True, silent=True) or {}
sid = req_json.get('cookieSiteId')
if not sid:
return fail_api(msg="数据错误")
res = curd.disable_status(CookieSite, sid)
if not res:
return fail_api(msg="操作失败")
return success_api(msg="已禁用")