diff --git a/applications/models/__init__.py b/applications/models/__init__.py
index 669d734..2f44513 100644
--- a/applications/models/__init__.py
+++ b/applications/models/__init__.py
@@ -14,3 +14,4 @@ from .admin_about import About
from .admin_friend import Friend
from .admin_stat import VisitLog, PageStat
from .admin_nav_click import NavClick
+from .admin_cookie import CookieUser, CookieSite
diff --git a/applications/models/admin_cookie.py b/applications/models/admin_cookie.py
new file mode 100644
index 0000000..95276a1
--- /dev/null
+++ b/applications/models/admin_cookie.py
@@ -0,0 +1,73 @@
+"""Cookie管理数据模型"""
+import datetime
+from applications.extensions import db
+
+
+class CookieUser(db.Model):
+ """登录用户 - 保存已登录用户的Cookie信息"""
+ __tablename__ = 'admin_cookie_user'
+
+ id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+ username = db.Column(db.String(100), nullable=False, comment='用户名')
+ nickname = db.Column(db.String(100), default='', comment='昵称')
+ website_url = db.Column(db.String(255), nullable=False, comment='网站URL')
+ website_name = db.Column(db.String(100), default='', comment='网站名称')
+ cookie_value = db.Column(db.Text, nullable=False, comment='Cookie值')
+ domain = db.Column(db.String(255), default='', comment='Cookie域名')
+ expiry = db.Column(db.DateTime, comment='过期时间')
+ enable = db.Column(db.Integer, default=1, comment='状态(1启用,0禁用)')
+ create_by = db.Column(db.String(50), default='admin', comment='创建者')
+ create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
+ update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
+
+ def to_dict(self):
+ return {
+ 'id': self.id,
+ 'username': self.username,
+ 'nickname': self.nickname,
+ 'website_url': self.website_url,
+ 'website_name': self.website_name,
+ 'cookie_value': self.cookie_value,
+ 'domain': self.domain,
+ 'expiry': self.expiry.strftime('%Y-%m-%d %H:%M:%S') if self.expiry else '',
+ 'enable': self.enable,
+ 'create_by': self.create_by,
+ 'create_at': self.create_at.strftime('%Y-%m-%d %H:%M:%S') if self.create_at else '',
+ 'update_at': self.update_at.strftime('%Y-%m-%d %H:%M:%S') if self.update_at else '',
+ }
+
+
+class CookieSite(db.Model):
+ """登录网站 - 保存网站的Cookie信息"""
+ __tablename__ = 'admin_cookie_site'
+
+ id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+ name = db.Column(db.String(100), nullable=False, comment='网站名称')
+ url = db.Column(db.String(255), nullable=False, comment='网站URL')
+ cookie_value = db.Column(db.Text, nullable=False, comment='Cookie值')
+ domain = db.Column(db.String(255), default='', comment='Cookie域名')
+ path = db.Column(db.String(255), default='/', comment='Cookie路径')
+ expiry = db.Column(db.DateTime, comment='过期时间')
+ secure = db.Column(db.Integer, default=0, comment='是否安全')
+ http_only = db.Column(db.Integer, default=0, comment='是否HTTPOnly')
+ enable = db.Column(db.Integer, default=1, comment='状态(1启用,0禁用)')
+ create_by = db.Column(db.String(50), default='admin', comment='创建者')
+ create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
+ update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
+
+ def to_dict(self):
+ return {
+ 'id': self.id,
+ 'name': self.name,
+ 'url': self.url,
+ 'cookie_value': self.cookie_value,
+ 'domain': self.domain,
+ 'path': self.path,
+ 'expiry': self.expiry.strftime('%Y-%m-%d %H:%M:%S') if self.expiry else '',
+ 'secure': self.secure,
+ 'http_only': self.http_only,
+ 'enable': self.enable,
+ 'create_by': self.create_by,
+ 'create_at': self.create_at.strftime('%Y-%m-%d %H:%M:%S') if self.create_at else '',
+ 'update_at': self.update_at.strftime('%Y-%m-%d %H:%M:%S') if self.update_at else '',
+ }
diff --git a/applications/schemas/__init__.py b/applications/schemas/__init__.py
index f5abec3..6eaea9a 100644
--- a/applications/schemas/__init__.py
+++ b/applications/schemas/__init__.py
@@ -9,3 +9,4 @@ from .admin_mail import MailOutSchema
from .admin_nav import NavOutSchema, NavManageSchema
from .admin_nav_category import NavCategorySchema
from .admin_friend import FriendOutSchema, FriendManageSchema
+from .admin_cookie import CookieUserManageSchema, CookieSiteManageSchema
diff --git a/applications/schemas/admin_cookie.py b/applications/schemas/admin_cookie.py
new file mode 100644
index 0000000..f4237f9
--- /dev/null
+++ b/applications/schemas/admin_cookie.py
@@ -0,0 +1,21 @@
+"""Cookie管理Schema"""
+from flask_marshmallow.sqla import SQLAlchemyAutoSchema
+
+from applications.extensions import ma
+from applications.models import CookieUser, CookieSite
+
+
+class CookieUserManageSchema(SQLAlchemyAutoSchema):
+ """登录用户管理用:序列化为表格数据 / 表单回填"""
+ class Meta:
+ model = CookieUser
+ include_fk = True
+ load_instance = True
+
+
+class CookieSiteManageSchema(SQLAlchemyAutoSchema):
+ """登录网站管理用:序列化为表格数据 / 表单回填"""
+ class Meta:
+ model = CookieSite
+ include_fk = True
+ load_instance = True
diff --git a/migrations/versions/1230ff234df9_add_cookie_user_and_cookie_site_tables.py b/migrations/versions/1230ff234df9_add_cookie_user_and_cookie_site_tables.py
new file mode 100644
index 0000000..2a97892
--- /dev/null
+++ b/migrations/versions/1230ff234df9_add_cookie_user_and_cookie_site_tables.py
@@ -0,0 +1,78 @@
+"""add cookie_user and cookie_site tables
+
+Revision ID: 1230ff234df9
+Revises: 9ae98ba6025b
+Create Date: 2026-09-08 16:51:22.296613
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '1230ff234df9'
+down_revision = '9ae98ba6025b'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('admin_cookie_site',
+ sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column('name', sa.String(length=100), nullable=False, comment='网站名称'),
+ sa.Column('url', sa.String(length=255), nullable=False, comment='网站URL'),
+ sa.Column('cookie_value', sa.Text(), nullable=False, comment='Cookie值'),
+ sa.Column('domain', sa.String(length=255), nullable=True, comment='Cookie域名'),
+ sa.Column('path', sa.String(length=255), nullable=True, comment='Cookie路径'),
+ sa.Column('expiry', sa.DateTime(), nullable=True, comment='过期时间'),
+ sa.Column('secure', sa.Integer(), nullable=True, comment='是否安全'),
+ sa.Column('http_only', sa.Integer(), nullable=True, comment='是否HTTPOnly'),
+ sa.Column('enable', sa.Integer(), nullable=True, comment='状态(1启用,0禁用)'),
+ sa.Column('create_by', sa.String(length=50), nullable=True, comment='创建者'),
+ sa.Column('create_at', sa.DateTime(), nullable=True, comment='创建时间'),
+ sa.Column('update_at', sa.DateTime(), nullable=True, comment='更新时间'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('admin_cookie_user',
+ sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column('username', sa.String(length=100), nullable=False, comment='用户名'),
+ sa.Column('nickname', sa.String(length=100), nullable=True, comment='昵称'),
+ sa.Column('website_url', sa.String(length=255), nullable=False, comment='网站URL'),
+ sa.Column('website_name', sa.String(length=100), nullable=True, comment='网站名称'),
+ sa.Column('cookie_value', sa.Text(), nullable=False, comment='Cookie值'),
+ sa.Column('domain', sa.String(length=255), nullable=True, comment='Cookie域名'),
+ sa.Column('expiry', sa.DateTime(), nullable=True, comment='过期时间'),
+ sa.Column('enable', sa.Integer(), nullable=True, comment='状态(1启用,0禁用)'),
+ sa.Column('create_by', sa.String(length=50), nullable=True, comment='创建者'),
+ sa.Column('create_at', sa.DateTime(), nullable=True, comment='创建时间'),
+ sa.Column('update_at', sa.DateTime(), nullable=True, comment='更新时间'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.drop_table('admin_cookie_manager')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('admin_cookie_manager',
+ sa.Column('id', sa.INTEGER(), nullable=False),
+ sa.Column('website_id', sa.INTEGER(), nullable=False),
+ sa.Column('website_name', sa.VARCHAR(length=100), nullable=True),
+ sa.Column('website_url', sa.VARCHAR(length=255), nullable=True),
+ sa.Column('name', sa.VARCHAR(length=100), nullable=False),
+ sa.Column('value', sa.TEXT(), nullable=False),
+ sa.Column('domain', sa.VARCHAR(length=255), nullable=True),
+ sa.Column('path', sa.VARCHAR(length=255), nullable=True),
+ sa.Column('expiry', sa.DATETIME(), nullable=True),
+ sa.Column('secure', sa.INTEGER(), nullable=True),
+ sa.Column('http_only', sa.INTEGER(), nullable=True),
+ sa.Column('enable', sa.INTEGER(), nullable=True),
+ sa.Column('create_by', sa.VARCHAR(length=50), nullable=True),
+ sa.Column('create_at', sa.DATETIME(), nullable=True),
+ sa.Column('update_at', sa.DATETIME(), nullable=True),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.drop_table('admin_cookie_user')
+ op.drop_table('admin_cookie_site')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/9ae98ba6025b_add_cookie_manager_table.py b/migrations/versions/9ae98ba6025b_add_cookie_manager_table.py
new file mode 100644
index 0000000..7487af2
--- /dev/null
+++ b/migrations/versions/9ae98ba6025b_add_cookie_manager_table.py
@@ -0,0 +1,45 @@
+"""add cookie_manager table
+
+Revision ID: 9ae98ba6025b
+Revises: a47a5d2a3f1b
+Create Date: 2026-09-08 15:23:56.125419
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '9ae98ba6025b'
+down_revision = 'a47a5d2a3f1b'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('admin_cookie_manager',
+ sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='主键ID'),
+ sa.Column('website_id', sa.Integer(), nullable=False, comment='网站ID'),
+ sa.Column('website_name', sa.String(length=100), nullable=True, comment='网站名称'),
+ sa.Column('website_url', sa.String(length=255), nullable=True, comment='网站URL'),
+ sa.Column('name', sa.String(length=100), nullable=False, comment='Cookie名称'),
+ sa.Column('value', sa.Text(), nullable=False, comment='Cookie值'),
+ sa.Column('domain', sa.String(length=255), nullable=True, comment='Cookie域名'),
+ sa.Column('path', sa.String(length=255), nullable=True, comment='Cookie路径'),
+ sa.Column('expiry', sa.DateTime(), nullable=True, comment='过期时间'),
+ sa.Column('secure', sa.Integer(), nullable=True, comment='是否安全'),
+ sa.Column('http_only', sa.Integer(), nullable=True, comment='是否HTTPOnly'),
+ sa.Column('enable', sa.Integer(), nullable=True, comment='状态(1启用,0禁用)'),
+ sa.Column('create_by', sa.String(length=50), nullable=True, comment='创建者'),
+ sa.Column('create_at', sa.DateTime(), nullable=True, comment='创建时间'),
+ sa.Column('update_at', sa.DateTime(), nullable=True, comment='更新时间'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('admin_cookie_manager')
+ # ### end Alembic commands ###
diff --git a/plugins/cookieManager/__init__.json b/plugins/cookieManager/__init__.json
new file mode 100644
index 0000000..61c8595
--- /dev/null
+++ b/plugins/cookieManager/__init__.json
@@ -0,0 +1,5 @@
+{
+ "plugin_name": "Cookie管理",
+ "plugin_version": "1.0.0",
+ "plugin_description": "后台Cookie管理模块。管理网站的Cookie信息,支持增删改查、启用/禁用、按网站分组查看。"
+}
diff --git a/plugins/cookieManager/__init__.py b/plugins/cookieManager/__init__.py
new file mode 100644
index 0000000..0b8474e
--- /dev/null
+++ b/plugins/cookieManager/__init__.py
@@ -0,0 +1,17 @@
+"""
+Cookie管理插件入口
+
+- event_init:在 app 上注册以下蓝图
+ - /system/cookie-user/* 登录用户管理
+ - /system/cookie-site/* 登录网站管理
+"""
+from flask import Flask
+
+from .view.cookie_user import bp as cookie_user_bp
+from .view.cookie_site import bp as cookie_site_bp
+
+
+def event_init(app: Flask):
+ app.register_blueprint(cookie_user_bp)
+ app.register_blueprint(cookie_site_bp)
+ print(" * cookieManager: registered /system/cookie-user/* and /system/cookie-site/* blueprints")
diff --git a/plugins/cookieManager/models/__init__.py b/plugins/cookieManager/models/__init__.py
new file mode 100644
index 0000000..41ea5be
--- /dev/null
+++ b/plugins/cookieManager/models/__init__.py
@@ -0,0 +1,43 @@
+"""Cookie管理数据模型"""
+import datetime
+from applications.extensions import db
+
+
+class CookieManager(db.Model):
+ """Cookie管理表"""
+ __tablename__ = 'admin_cookie_manager'
+
+ id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='主键ID')
+ website_id = db.Column(db.Integer, nullable=False, comment='网站ID')
+ website_name = db.Column(db.String(100), default='', comment='网站名称')
+ website_url = db.Column(db.String(255), default='', comment='网站URL')
+ name = db.Column(db.String(100), nullable=False, comment='Cookie名称')
+ value = db.Column(db.Text, nullable=False, comment='Cookie值')
+ domain = db.Column(db.String(255), default='', comment='Cookie域名')
+ path = db.Column(db.String(255), default='/', comment='Cookie路径')
+ expiry = db.Column(db.DateTime, comment='过期时间')
+ secure = db.Column(db.Integer, default=0, comment='是否安全')
+ http_only = db.Column(db.Integer, default=0, comment='是否HTTPOnly')
+ enable = db.Column(db.Integer, default=1, comment='状态(1启用,0禁用)')
+ create_by = db.Column(db.String(50), default='admin', comment='创建者')
+ create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
+ update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
+
+ def to_dict(self):
+ return {
+ 'id': self.id,
+ 'website_id': self.website_id,
+ 'website_name': self.website_name,
+ 'website_url': self.website_url,
+ 'name': self.name,
+ 'value': self.value,
+ 'domain': self.domain,
+ 'path': self.path,
+ 'expiry': self.expiry.strftime('%Y-%m-%d %H:%M:%S') if self.expiry else '',
+ 'secure': self.secure,
+ 'http_only': self.http_only,
+ 'enable': self.enable,
+ 'create_by': self.create_by,
+ 'create_at': self.create_at.strftime('%Y-%m-%d %H:%M:%S') if self.create_at else '',
+ 'update_at': self.update_at.strftime('%Y-%m-%d %H:%M:%S') if self.update_at else '',
+ }
diff --git a/plugins/cookieManager/schemas/__init__.py b/plugins/cookieManager/schemas/__init__.py
new file mode 100644
index 0000000..08829ce
--- /dev/null
+++ b/plugins/cookieManager/schemas/__init__.py
@@ -0,0 +1,13 @@
+"""Cookie管理Schema"""
+from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
+
+from applications.extensions import db
+from plugins.cookieManager.models import CookieManager
+
+
+class CookieManageSchema(SQLAlchemyAutoSchema):
+ """后台管理用:序列化为表格数据 / 表单回填"""
+ class Meta:
+ model = CookieManager
+ include_fk = True
+ load_instance = True
diff --git a/plugins/cookieManager/templates/system/cookie_site/add.html b/plugins/cookieManager/templates/system/cookie_site/add.html
new file mode 100644
index 0000000..720e781
--- /dev/null
+++ b/plugins/cookieManager/templates/system/cookie_site/add.html
@@ -0,0 +1,125 @@
+
+
+
+
+ 新增登录网站
+ {% include 'system/common/header.html' %}
+
+
+
+
+
+
+
diff --git a/plugins/cookieManager/templates/system/cookie_site/edit.html b/plugins/cookieManager/templates/system/cookie_site/edit.html
new file mode 100644
index 0000000..a2137b8
--- /dev/null
+++ b/plugins/cookieManager/templates/system/cookie_site/edit.html
@@ -0,0 +1,128 @@
+
+
+
+
+ 编辑登录网站
+ {% include 'system/common/header.html' %}
+
+
+
+
+
+
+
diff --git a/plugins/cookieManager/templates/system/cookie_site/main.html b/plugins/cookieManager/templates/system/cookie_site/main.html
new file mode 100644
index 0000000..aa1e01b
--- /dev/null
+++ b/plugins/cookieManager/templates/system/cookie_site/main.html
@@ -0,0 +1,163 @@
+
+
+
+
+ 登录网站
+ {% include 'system/common/header.html' %}
+
+
+
+
+
+
+{% include 'system/common/footer.html' %}
+{% raw %}
+
+
+
+
+
+{% endraw %}
+
+
+
+
diff --git a/plugins/cookieManager/templates/system/cookie_user/add.html b/plugins/cookieManager/templates/system/cookie_user/add.html
new file mode 100644
index 0000000..479b6ce
--- /dev/null
+++ b/plugins/cookieManager/templates/system/cookie_user/add.html
@@ -0,0 +1,118 @@
+
+
+
+
+ 新增登录用户
+ {% include 'system/common/header.html' %}
+
+
+
+
+
+
+
diff --git a/plugins/cookieManager/templates/system/cookie_user/edit.html b/plugins/cookieManager/templates/system/cookie_user/edit.html
new file mode 100644
index 0000000..6378684
--- /dev/null
+++ b/plugins/cookieManager/templates/system/cookie_user/edit.html
@@ -0,0 +1,120 @@
+
+
+
+
+ 编辑登录用户
+ {% include 'system/common/header.html' %}
+
+
+
+
+
+
+
diff --git a/plugins/cookieManager/templates/system/cookie_user/main.html b/plugins/cookieManager/templates/system/cookie_user/main.html
new file mode 100644
index 0000000..4503b37
--- /dev/null
+++ b/plugins/cookieManager/templates/system/cookie_user/main.html
@@ -0,0 +1,158 @@
+
+
+
+
+ 登录用户
+ {% include 'system/common/header.html' %}
+
+
+
+
+
+
+{% include 'system/common/footer.html' %}
+{% raw %}
+
+
+
+
+
+{% endraw %}
+
+
+
+
diff --git a/plugins/cookieManager/view/cookie.py b/plugins/cookieManager/view/cookie.py
new file mode 100644
index 0000000..0f13684
--- /dev/null
+++ b/plugins/cookieManager/view/cookie.py
@@ -0,0 +1,220 @@
+"""
+Cookie管理视图 — 插件版
+
+URL 前缀:/system/cookie
+模型和Schema从插件内部导入,避免循环依赖。
+"""
+import os
+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
+
+# 从插件内部导入模型和Schema(避免在applications.models中导入导致循环依赖)
+from plugins.cookieManager.models import CookieManager
+from plugins.cookieManager.schemas import CookieManageSchema
+
+dir_path = os.path.dirname(os.path.abspath(__file__))
+bp = Blueprint(
+ 'cookie_manager', __name__,
+ url_prefix='/system/cookie',
+ template_folder=os.path.join(dir_path, '..', 'templates'),
+)
+
+
+@bp.get('/')
+@authorize("system:cookie:main")
+def main():
+ return render_template('system/cookie/main.html')
+
+
+@bp.get('/data')
+@limiter.limit("60 per minute")
+@authorize("system:cookie:main")
+def data():
+ keyword = str_escape(request.args.get('keyword', type=str))
+ website_id = request.args.get('website_id', type=int)
+
+ query = CookieManager.query.filter()
+ if keyword:
+ query = query.filter(
+ db.or_(
+ CookieManager.name.contains(keyword),
+ CookieManager.website_name.contains(keyword),
+ CookieManager.website_url.contains(keyword),
+ )
+ )
+ if website_id:
+ query = query.filter(CookieManager.website_id == website_id)
+
+ items = query.order_by(CookieManager.id.desc()).layui_paginate()
+ return table_api(
+ msg="请求成功",
+ data=curd.model_to_dicts(schema=CookieManageSchema, data=items.items),
+ count=items.total,
+ )
+
+
+@bp.get('/add')
+@authorize("system:cookie:add", log=True)
+def add():
+ return render_template('system/cookie/add.html')
+
+
+@bp.post('/save')
+@authorize("system:cookie:add", log=True)
+def save():
+ req_json = request.get_json(force=True, silent=True) or {}
+ website_id = int(req_json.get('website_id') or 0)
+ website_name = str_escape(req_json.get('website_name') or '')
+ website_url = str_escape(req_json.get('website_url') or '')
+ name = str_escape(req_json.get('name') or '')
+ value = req_json.get('value') or ''
+ domain = str_escape(req_json.get('domain') or '')
+ path = str_escape(req_json.get('path') or '/')
+ expiry_str = req_json.get('expiry') or ''
+ 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)
+
+ if not all([website_id, name, value]):
+ return fail_api(msg="网站ID/Cookie名称/值不得为空")
+
+ # 解析过期时间
+ expiry = None
+ if expiry_str:
+ try:
+ expiry = datetime.datetime.strptime(expiry_str, '%Y-%m-%d %H:%M:%S')
+ except ValueError:
+ try:
+ expiry = datetime.datetime.fromisoformat(expiry_str)
+ except ValueError:
+ pass
+
+ try:
+ cookie = CookieManager(
+ website_id=website_id,
+ website_name=website_name,
+ website_url=website_url,
+ name=name,
+ value=value,
+ domain=domain,
+ path=path,
+ expiry=expiry,
+ secure=secure,
+ http_only=http_only,
+ enable=enable,
+ create_by=current_user.username if current_user.is_authenticated else 'admin',
+ )
+ db.session.add(cookie)
+ 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:edit", log=True)
+def edit():
+ cookie_id = request.args.get('cookieId', type=int)
+ cookie = curd.get_one_by_id(CookieManager, cookie_id)
+ if not cookie:
+ return fail_api(msg="Cookie不存在")
+ return render_template('system/cookie/edit.html', cookie=cookie)
+
+
+@bp.put('/update')
+@authorize("system:cookie:edit", log=True)
+def update():
+ req_json = request.get_json(force=True, silent=True) or {}
+ cookie_id = req_json.get('cookieId')
+ cookie = curd.get_one_by_id(CookieManager, cookie_id)
+ if not cookie:
+ return fail_api(msg="Cookie不存在")
+
+ website_id = int(req_json.get('website_id') or cookie.website_id)
+ website_name = str_escape(req_json.get('website_name') or cookie.website_name)
+ website_url = str_escape(req_json.get('website_url') or cookie.website_url)
+ name = str_escape(req_json.get('name') or '')
+ value = req_json.get('value') or ''
+ domain = str_escape(req_json.get('domain') or cookie.domain)
+ path = str_escape(req_json.get('path') or cookie.path)
+ secure = int(req_json.get('secure') or cookie.secure)
+ http_only = int(req_json.get('http_only') or cookie.http_only)
+ enable = int(req_json.get('enable') or cookie.enable)
+
+ if not all([website_id, name, value]):
+ return fail_api(msg="网站ID/Cookie名称/值不得为空")
+
+ # 解析过期时间
+ expiry_str = req_json.get('expiry') or ''
+ expiry = None
+ if expiry_str:
+ try:
+ expiry = datetime.datetime.strptime(expiry_str, '%Y-%m-%d %H:%M:%S')
+ except ValueError:
+ try:
+ expiry = datetime.datetime.fromisoformat(expiry_str)
+ except ValueError:
+ pass
+
+ try:
+ cookie.website_id = website_id
+ cookie.website_name = website_name
+ cookie.website_url = website_url
+ cookie.name = name
+ cookie.value = value
+ cookie.domain = domain
+ cookie.path = path
+ cookie.expiry = expiry
+ cookie.secure = secure
+ cookie.http_only = http_only
+ cookie.enable = enable
+ db.session.commit()
+ except Exception as e:
+ db.session.rollback()
+ return fail_api(msg=f"更新失败:{e}")
+ return success_api(msg="更新成功")
+
+
+@bp.delete('/remove/')
+@authorize("system:cookie:remove", log=True)
+def remove(cookie_id):
+ res = curd.delete_one_by_id(CookieManager, cookie_id)
+ if not res:
+ return fail_api(msg="删除失败")
+ return success_api(msg="删除成功")
+
+
+@bp.put('/enable')
+@authorize("system:cookie:edit", log=True)
+def enable():
+ req_json = request.get_json(force=True, silent=True) or {}
+ cookie_id = req_json.get('cookieId')
+ if not cookie_id:
+ return fail_api(msg="数据错误")
+ res = curd.enable_status(CookieManager, cookie_id)
+ if not res:
+ return fail_api(msg="操作失败")
+ return success_api(msg="已启用")
+
+
+@bp.put('/disable')
+@authorize("system:cookie:edit", log=True)
+def dis_enable():
+ req_json = request.get_json(force=True, silent=True) or {}
+ cookie_id = req_json.get('cookieId')
+ if not cookie_id:
+ return fail_api(msg="数据错误")
+ res = curd.disable_status(CookieManager, cookie_id)
+ if not res:
+ return fail_api(msg="操作失败")
+ return success_api(msg="已禁用")
diff --git a/plugins/cookieManager/view/cookie_site.py b/plugins/cookieManager/view/cookie_site.py
new file mode 100644
index 0000000..923238a
--- /dev/null
+++ b/plugins/cookieManager/view/cookie_site.py
@@ -0,0 +1,160 @@
+"""
+Cookie管理 - 登录网站视图
+
+URL: /system/cookie-site
+"""
+import os
+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
+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'))
+
+
+@bp.get('/')
+@authorize("system:cookie-site:main")
+def main():
+ return render_template('system/cookie_site/main.html')
+
+
+@bp.get('/data')
+@limiter.limit("60 per minute")
+@authorize("system:cookie-site:main")
+def data():
+ keyword = str_escape(request.args.get('keyword', type=str))
+ query = CookieSite.query.filter()
+ if keyword:
+ query = query.filter(
+ db.or_(
+ CookieSite.name.contains(keyword),
+ CookieSite.url.contains(keyword),
+ )
+ )
+ items = query.order_by(CookieSite.id.desc()).layui_paginate()
+ 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')
+
+
+@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(
+ name=name,
+ url=url,
+ cookie_value=cookie_value,
+ domain=str_escape(req_json.get('domain')),
+ path=str_escape(req_json.get('path')) or '/',
+ 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)
+
+
+@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.name = name
+ obj.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 = 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/')
+@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="已禁用")
diff --git a/plugins/cookieManager/view/cookie_user.py b/plugins/cookieManager/view/cookie_user.py
new file mode 100644
index 0000000..e611b42
--- /dev/null
+++ b/plugins/cookieManager/view/cookie_user.py
@@ -0,0 +1,159 @@
+"""
+Cookie管理 - 登录用户视图
+
+URL: /system/cookie-user
+"""
+import os
+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 CookieUser
+from applications.schemas import CookieUserManageSchema
+
+dir_path = os.path.dirname(os.path.abspath(__file__))
+bp = Blueprint('cookie_user', __name__,
+ url_prefix='/system/cookie-user',
+ template_folder=os.path.join(dir_path, '..', 'templates'))
+
+
+@bp.get('/')
+@authorize("system:cookie-user:main")
+def main():
+ return render_template('system/cookie_user/main.html')
+
+
+@bp.get('/data')
+@limiter.limit("60 per minute")
+@authorize("system:cookie-user:main")
+def data():
+ keyword = str_escape(request.args.get('keyword', type=str))
+ query = CookieUser.query.filter()
+ if keyword:
+ query = query.filter(
+ db.or_(
+ CookieUser.username.contains(keyword),
+ CookieUser.website_name.contains(keyword),
+ CookieUser.website_url.contains(keyword),
+ )
+ )
+ items = query.order_by(CookieUser.id.desc()).layui_paginate()
+ return table_api(
+ msg="请求成功",
+ data=curd.model_to_dicts(schema=CookieUserManageSchema, data=items.items),
+ count=items.total,
+ )
+
+
+@bp.get('/add')
+@authorize("system:cookie-user:add", log=True)
+def add():
+ return render_template('system/cookie_user/add.html')
+
+
+@bp.post('/save')
+@authorize("system:cookie-user:add", log=True)
+def save():
+ req_json = request.get_json(force=True, silent=True) or {}
+ username = str_escape(req_json.get('username'))
+ website_url = str_escape(req_json.get('website_url'))
+ cookie_value = str_escape(req_json.get('cookie_value'))
+ if not all([username, website_url, cookie_value]):
+ return fail_api(msg="用户名/网站URL/Cookie值不得为空")
+ try:
+ obj = CookieUser(
+ username=username,
+ nickname=str_escape(req_json.get('nickname')),
+ website_url=website_url,
+ website_name=str_escape(req_json.get('website_name')),
+ cookie_value=cookie_value,
+ domain=str_escape(req_json.get('domain')),
+ expiry=req_json.get('expiry'),
+ 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-user:edit", log=True)
+def edit():
+ cid = request.args.get('cookieUserId', type=int)
+ obj = curd.get_one_by_id(CookieUser, cid)
+ if not obj:
+ return fail_api(msg="记录不存在")
+ return render_template('system/cookie_user/edit.html', obj=obj)
+
+
+@bp.put('/update')
+@authorize("system:cookie-user:edit", log=True)
+def update():
+ req_json = request.get_json(force=True, silent=True) or {}
+ cid = req_json.get('cookieUserId')
+ obj = curd.get_one_by_id(CookieUser, cid)
+ if not obj:
+ return fail_api(msg="记录不存在")
+ username = str_escape(req_json.get('username'))
+ website_url = str_escape(req_json.get('website_url'))
+ cookie_value = str_escape(req_json.get('cookie_value'))
+ if not all([username, website_url, cookie_value]):
+ return fail_api(msg="用户名/网站URL/Cookie值不得为空")
+ try:
+ obj.username = username
+ obj.nickname = str_escape(req_json.get('nickname'))
+ obj.website_url = website_url
+ obj.website_name = str_escape(req_json.get('website_name'))
+ obj.cookie_value = cookie_value
+ obj.domain = str_escape(req_json.get('domain'))
+ obj.expiry = req_json.get('expiry')
+ 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/')
+@authorize("system:cookie-user:remove", log=True)
+def remove(cid):
+ res = curd.delete_one_by_id(CookieUser, cid)
+ if not res:
+ return fail_api(msg="删除失败")
+ return success_api(msg="删除成功")
+
+
+@bp.put('/enable')
+@authorize("system:cookie-user:edit", log=True)
+def enable():
+ req_json = request.get_json(force=True, silent=True) or {}
+ cid = req_json.get('cookieUserId')
+ if not cid:
+ return fail_api(msg="数据错误")
+ res = curd.enable_status(CookieUser, cid)
+ if not res:
+ return fail_api(msg="操作失败")
+ return success_api(msg="已启用")
+
+
+@bp.put('/disable')
+@authorize("system:cookie-user:edit", log=True)
+def disable():
+ req_json = request.get_json(force=True, silent=True) or {}
+ cid = req_json.get('cookieUserId')
+ if not cid:
+ return fail_api(msg="数据错误")
+ res = curd.disable_status(CookieUser, cid)
+ if not res:
+ return fail_api(msg="操作失败")
+ return success_api(msg="已禁用")