更新插件功能
This commit is contained in:
@@ -5,6 +5,18 @@ from urllib.parse import quote_plus as urlquote
|
||||
from apscheduler.executors.pool import ThreadPoolExecutor
|
||||
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
||||
|
||||
# 强制读入 flaskenv 中的环境变量
|
||||
with open(".flaskenv", "r", encoding='utf-8') as f:
|
||||
for line in f.read().split("\n"):
|
||||
pos = line.find("#")
|
||||
if pos != -1:
|
||||
line = line[:pos]
|
||||
line = line.strip()
|
||||
if line == "":
|
||||
continue
|
||||
_ = line.split("=")
|
||||
key, value = _[0], '='.join(_[1:])
|
||||
os.environ[key.strip()] = value.strip()
|
||||
|
||||
class BaseConfig:
|
||||
|
||||
@@ -75,6 +87,9 @@ class BaseConfig:
|
||||
'max_instances': 3
|
||||
}
|
||||
|
||||
# 插件配置
|
||||
PLUGIN_ENABLE_FOLDERS = os.getenv('PLUGIN_ENABLE_FOLDERS')
|
||||
|
||||
# 配置多个数据库连接的连接串写法示例
|
||||
# HOSTNAME: 指数据库的IP地址、USERNAME:指数据库登录的用户名、PASSWORD:指数据库登录密码、PORT:指数据库开放的端口、DATABASE:指需要连接的数据库名称
|
||||
# MSSQL: f"mssql+pymssql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=cp936"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
from applications.dev import user
|
||||
from applications.dev import role
|
||||
from applications.dev import power
|
||||
from applications.dev import department
|
||||
from applications.dev import console
|
||||
from flask import Flask
|
||||
|
||||
# 获取app应用实例,会被初始化插件时重新赋值
|
||||
app = None # type: Flask
|
||||
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
输出控制台日志
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
import ctypes
|
||||
|
||||
NONE = "\033[m"
|
||||
RED = "\033[0;32;31m"
|
||||
LIGHT_RED = "\033[1;31m"
|
||||
GREEN = "\033[0;32;32m"
|
||||
LIGHT_GREEN = "\033[1;32m"
|
||||
BLUE = "\033[0;32;34m"
|
||||
LIGHT_BLUE = "\033[1;34m"
|
||||
DARY_GRAY = "\033[1;30m"
|
||||
CYAN = "\033[0;36m"
|
||||
LIGHT_CYAN = "\033[1;36m"
|
||||
PURPLE = "\033[0;35m"
|
||||
LIGHT_PURPLE = "\033[1;35m"
|
||||
BROWN = "\033[0;33m"
|
||||
YELLOW = "\033[1;33m"
|
||||
LIGHT_GRAY = "\033[0;37m"
|
||||
WHITE = "\033[1;37m"
|
||||
|
||||
# 开启 Windows 下对于 ESC控制符 的支持
|
||||
if sys.platform == "win32":
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
||||
|
||||
|
||||
def _print(level, msg):
|
||||
time_ = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
|
||||
level_name = {10: "Plain",
|
||||
11: "Log",
|
||||
12: "Info",
|
||||
13: "Debug",
|
||||
14: "Success",
|
||||
15: "Warning",
|
||||
16: "Error"}
|
||||
|
||||
color = {10: NONE,
|
||||
11: LIGHT_CYAN,
|
||||
12: LIGHT_BLUE,
|
||||
13: PURPLE,
|
||||
14: GREEN,
|
||||
15: YELLOW,
|
||||
16: RED}
|
||||
|
||||
print(f'{color.get(level, NONE)}[{time_}]({level_name.get(level, "Plain")}):', msg, f"{NONE}")
|
||||
|
||||
|
||||
def plain(*args, sep=' '):
|
||||
msg = sep.join(str(_) for _ in args)
|
||||
_print(10, msg)
|
||||
|
||||
|
||||
def log(*args, sep=' '):
|
||||
msg = sep.join(str(_) for _ in args)
|
||||
_print(11, msg)
|
||||
|
||||
|
||||
def info(*args, sep=' '):
|
||||
msg = sep.join(str(_) for _ in args)
|
||||
_print(12, msg)
|
||||
|
||||
|
||||
def debug(*args, sep=' '):
|
||||
msg = sep.join(str(_) for _ in args)
|
||||
_print(13, msg)
|
||||
|
||||
|
||||
def success(*args, sep=' '):
|
||||
msg = sep.join(str(_) for _ in args)
|
||||
_print(14, msg)
|
||||
|
||||
|
||||
def warn(*args):
|
||||
warning(*args)
|
||||
|
||||
|
||||
def warning(*args, sep=' '):
|
||||
msg = sep.join(str(_) for _ in args)
|
||||
_print(15, msg)
|
||||
|
||||
|
||||
def error(*args, sep=' '):
|
||||
msg = sep.join(str(_) for _ in args)
|
||||
_print(16, msg)
|
||||
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
集成了对 Pear Admin Flask 的部门的操作,并给了相对应的示例。
|
||||
"""
|
||||
|
||||
from applications.common import curd
|
||||
from applications.extensions import db
|
||||
from applications.models import Dept, User
|
||||
from applications.schemas import DeptOutSchema
|
||||
|
||||
|
||||
def get_all():
|
||||
"""
|
||||
获取全部权限,会返回一个列表,每个列表是一个字典。
|
||||
|
||||
字典构成如下::
|
||||
|
||||
{
|
||||
"address":"这是总公司", # 地址
|
||||
"deptId":1, # 公司ID
|
||||
"deptName":"总公司", # 公司名
|
||||
"email":"1", # 公司 email
|
||||
"leader":"", # 公司领导人
|
||||
"parentId":0, # 父ID
|
||||
"phone":"", # 联系电话
|
||||
"sort":1, # 排序
|
||||
"status":"1" # 状态 1-开启 0-关闭
|
||||
}
|
||||
|
||||
:return: 列表
|
||||
"""
|
||||
dept = Dept.query.order_by(Dept.sort).all()
|
||||
return curd.model_to_dicts(schema=DeptOutSchema, data=dept)
|
||||
|
||||
|
||||
def add(parentId, deptName, sort, leader, phone, email, status, address):
|
||||
"""
|
||||
添加一个公司
|
||||
|
||||
:param parentId: 父公司ID,0未总公司
|
||||
:param deptName: 公司名称
|
||||
:param sort: 排序
|
||||
:param leader: 负责人
|
||||
:param phone: 手机
|
||||
:param email: 邮箱
|
||||
:param status: 状态 1-打开 0-关闭
|
||||
:param address: 地址
|
||||
:return: 是否成功
|
||||
"""
|
||||
dept = Dept(
|
||||
parent_id=parentId,
|
||||
dept_name=deptName,
|
||||
sort=sort,
|
||||
leader=leader,
|
||||
phone=phone,
|
||||
email=email,
|
||||
status=status,
|
||||
address=address
|
||||
)
|
||||
r = db.session.add(dept)
|
||||
db.session.commit()
|
||||
if r:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def update(deptId, data):
|
||||
"""
|
||||
更新公司信息
|
||||
|
||||
可更新内容如下::
|
||||
|
||||
"dept_name"
|
||||
"sort"
|
||||
"leader"
|
||||
"phone"
|
||||
"email"
|
||||
"status"
|
||||
"address"
|
||||
|
||||
:param deptId: 公司ID
|
||||
:param data: 要更新公司字典
|
||||
:return: 是否成功
|
||||
"""
|
||||
d = Dept.query.filter_by(id=deptId).update(data)
|
||||
if not d:
|
||||
return False
|
||||
db.session.commit()
|
||||
return True
|
||||
|
||||
|
||||
def delete(deptId):
|
||||
"""
|
||||
删除公司
|
||||
|
||||
:param deptId: 公司ID
|
||||
:return: 是否成功
|
||||
"""
|
||||
d = Dept.query.filter_by(id=deptId).delete()
|
||||
if not d:
|
||||
return False
|
||||
res = User.query.filter_by(dept_id=deptId).update({"dept_id": None})
|
||||
db.session.commit()
|
||||
if res:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
集成了对 Pear Admin Flask 二次开发的的邮件操作,并给了相对应的示例。
|
||||
"""
|
||||
from flask import current_app
|
||||
from flask_mail import Message
|
||||
|
||||
from applications.common.curd import model_to_dicts
|
||||
from applications.common.helper import ModelFilter
|
||||
from applications.extensions import db, flask_mail
|
||||
from applications.models import Mail
|
||||
from applications.schemas import MailOutSchema
|
||||
|
||||
|
||||
def get_all(receiver=None, subject=None, content=None):
|
||||
"""
|
||||
获取邮件
|
||||
|
||||
返回的列表中的字典构造如下::
|
||||
|
||||
{
|
||||
"content": "", # html内容
|
||||
"create_at": "2022-12-25T10:51:17", # 时间
|
||||
"id": 17, # 邮件ID
|
||||
"realname": "超级管理", # 创建者
|
||||
"receiver": "", # 接收者
|
||||
"subject": "" # 主题
|
||||
}
|
||||
|
||||
:param receiver: 发送者
|
||||
:param subject: 邮件标题
|
||||
:param content: 邮件内容
|
||||
:return: 列表
|
||||
"""
|
||||
# 查询参数构造
|
||||
mf = ModelFilter()
|
||||
if receiver:
|
||||
mf.contains(field_name="receiver", value=receiver)
|
||||
if subject:
|
||||
mf.contains(field_name="subject", value=subject)
|
||||
if content:
|
||||
mf.exact(field_name="content", value=content)
|
||||
# orm查询
|
||||
# 使用分页获取data需要.items
|
||||
mail = Mail.query.filter(mf.get_filter(Mail)).layui_paginate()
|
||||
return model_to_dicts(schema=MailOutSchema, data=mail.items)
|
||||
|
||||
|
||||
def add(receiver, subject, content, user_id):
|
||||
"""
|
||||
发送一封邮件,若发送成功立刻提交数据库。
|
||||
|
||||
:param receiver: 接收者 多个用英文逗号隔开
|
||||
:param subject: 邮件主题
|
||||
:param content: 邮件 html
|
||||
:param user_id: 发送用户ID(谁发送的?) 可以用 from flask_login import current_user ; current_user.id 来表示当前登录用户
|
||||
:return: 成功与否
|
||||
"""
|
||||
try:
|
||||
msg = Message(subject=subject, recipients=receiver.split(";"), html=content)
|
||||
flask_mail.send(msg)
|
||||
except BaseException as e:
|
||||
current_app.log_exception(e)
|
||||
return False
|
||||
|
||||
mail = Mail(receiver=receiver, subject=subject, content=content, user_id=user_id)
|
||||
|
||||
db.session.add(mail)
|
||||
db.session.commit()
|
||||
return True
|
||||
|
||||
|
||||
def delete(id):
|
||||
"""
|
||||
删除邮件记录,立刻写入数据库。
|
||||
|
||||
:param id: 邮件ID
|
||||
:return: 成功与否
|
||||
"""
|
||||
res = Mail.query.filter_by(id=id).delete()
|
||||
if not res:
|
||||
return False
|
||||
db.session.commit()
|
||||
return True
|
||||
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
集成了对 Pear Admin Flask 的权限操作,并给了相对应的示例。
|
||||
注意:此权限相当于添加后台菜单。
|
||||
"""
|
||||
|
||||
from applications.common import curd
|
||||
from applications.extensions import db
|
||||
from applications.models import Power
|
||||
|
||||
from applications.extensions import ma
|
||||
from marshmallow import fields
|
||||
|
||||
|
||||
class PowerOutSchema2(ma.Schema): # 序列化类
|
||||
powerId = fields.Str(attribute="id")
|
||||
powerName = fields.Str(attribute="name")
|
||||
powerType = fields.Str(attribute="type")
|
||||
powerUrl = fields.Str(attribute="url")
|
||||
powerCode = fields.Str(attribute="code")
|
||||
openType = fields.Str(attribute="open_type")
|
||||
parentId = fields.Str(attribute="parent_id")
|
||||
icon = fields.Str()
|
||||
sort = fields.Integer()
|
||||
create_time = fields.DateTime()
|
||||
update_time = fields.DateTime()
|
||||
enable = fields.Integer()
|
||||
|
||||
|
||||
def get_all():
|
||||
"""
|
||||
获取所有权限,会返回一个含有菜单的列表。每个列表都是一个字典。
|
||||
|
||||
字典构成如下::
|
||||
{
|
||||
"powerType": "0", # 权限类型 0目录 1菜单 2按钮
|
||||
"powerUrl": None, # 路径
|
||||
"powerCode": "", # 权限标识
|
||||
"update_time": None, # 更新时间
|
||||
"sort": 1, # 排序
|
||||
"openType": None, # 打开方式 _iframe框架 _blank新页面
|
||||
"icon": "layui-icon layui-icon-set-fill", # 图标
|
||||
"powerName": "系统管理", # 名称
|
||||
"create_time": None, # 创建时间
|
||||
"parentId": "0", # 父id
|
||||
"powerId": "1", # 自己的id
|
||||
"enable": 1 # 是否启用
|
||||
}
|
||||
|
||||
:return: 菜单列表。
|
||||
"""
|
||||
power = Power.query.all()
|
||||
res = curd.model_to_dicts(schema=PowerOutSchema2, data=power)
|
||||
res.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
|
||||
return res
|
||||
|
||||
|
||||
def add(parentId, powerName, powerType, icon, sort: int, enable: bool, powerCode="", powerUrl="", openType=""):
|
||||
"""
|
||||
新建一个菜单权限。
|
||||
|
||||
参考代码::
|
||||
|
||||
dev.power.add("0", "测试", "1", "layui-icon-time", 0, True, "testfor", "https://baidu.com", "_iframe")
|
||||
|
||||
:param parentId: 父ID,0为顶级菜单ID
|
||||
:param powerName: 菜单名称
|
||||
:param powerType: 权限类型(状态) 0目录 1菜单 2按钮
|
||||
:param icon: 图标,详细查看layui的图标
|
||||
:param sort: 排序
|
||||
:param enable: 是否启用
|
||||
|
||||
:param powerCode: 权限标识
|
||||
:param powerUrl: 权限URL,菜单打开的网址,或者是路径。可选,菜单和按钮类型必填。
|
||||
:param openType: 打开方式,_iframe框架 _blank新页面。可选,菜单和按钮类型必填。
|
||||
|
||||
|
||||
|
||||
:return: 返回新权限ID
|
||||
"""
|
||||
power = Power(
|
||||
icon=icon,
|
||||
open_type=openType,
|
||||
parent_id=parentId,
|
||||
code=powerCode,
|
||||
name=powerName,
|
||||
type=powerType,
|
||||
url=powerUrl,
|
||||
sort=sort,
|
||||
enable=1
|
||||
)
|
||||
r = db.session.add(power)
|
||||
db.session.commit()
|
||||
return power.id
|
||||
|
||||
|
||||
def update(powerId, data):
|
||||
"""
|
||||
更新权限。
|
||||
|
||||
data可选::
|
||||
|
||||
"icon"
|
||||
"open_type"
|
||||
"parent_id"
|
||||
"code"
|
||||
"name"
|
||||
"type"
|
||||
"url"
|
||||
"sort"
|
||||
|
||||
:param powerId: 要更新的权限ID
|
||||
|
||||
:return: 是否成功
|
||||
"""
|
||||
res = Power.query.filter_by(id=powerId).update(data)
|
||||
db.session.commit()
|
||||
if res:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def delete(powerId):
|
||||
"""
|
||||
删除权限。
|
||||
|
||||
:param powerId: 要更新的权限ID
|
||||
:return: 是否成功
|
||||
"""
|
||||
power = Power.query.filter_by(id=powerId).first()
|
||||
power.role = []
|
||||
|
||||
r = Power.query.filter_by(id=powerId).delete()
|
||||
db.session.commit()
|
||||
if r:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -0,0 +1,157 @@
|
||||
"""
|
||||
集成了对 Pear Admin Flask 的角色操作,并给了相对应的示例。
|
||||
"""
|
||||
from applications.extensions import db
|
||||
from applications.models import Role, Power
|
||||
from applications.schemas import PowerOutSchema2
|
||||
|
||||
|
||||
def filter_by(**kwargs):
|
||||
"""
|
||||
检索角色字段信息,可用于获取角色ID等。系统中默认管理员角色id为1,普通用户为2。
|
||||
内部采用的是使用 Role.query.filter_by(**kwargs) 进行数据库查询。
|
||||
|
||||
注意:此函数返回的结果为构造的 SQL的查询字符串 ,以 role_filter 命名,但是并不是用户数据。
|
||||
|
||||
返回的字段如下::
|
||||
|
||||
id name code enable remark details sort create_time update_time power
|
||||
具体参考 applications/models/admin_role.py 中的模型定义。
|
||||
|
||||
参考调用如下::
|
||||
|
||||
roleinfo = dev.role.filter_by(code='admin').first() # 第一个符合要求的角色信息
|
||||
print(role.id, role.name) # 输出角色名称与角色标识
|
||||
|
||||
# 找出所有角色id
|
||||
for role in dev.role.filter_by().all():
|
||||
print(role.id, role.name)
|
||||
|
||||
:param kwargs: 查询参数
|
||||
:return: 角色SQL的查询字符串
|
||||
"""
|
||||
return Role.query.filter_by(**kwargs)
|
||||
|
||||
|
||||
def add(roleName, roleCode, enable, sort, details):
|
||||
"""
|
||||
添加一个角色。此函数直接写入数据库。此函数不会检测角色是否已经存在(官方在API接口中也没有检测)。
|
||||
|
||||
:param roleName: 角色名称 (如管理员)
|
||||
:param roleCode: 角色标识 (如admin)
|
||||
:param enable: 是否启用 True or False
|
||||
:param sort: 排序
|
||||
:param details: 描述
|
||||
:return: None
|
||||
"""
|
||||
role = Role(
|
||||
details=details,
|
||||
enable=enable,
|
||||
code=roleCode,
|
||||
name=roleName,
|
||||
sort=sort
|
||||
)
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_power(role_filter, detail=False, p=0):
|
||||
"""
|
||||
获取角色的权限。
|
||||
|
||||
如果是非详细数据,会返回一个含有权限id的列表。
|
||||
如果是详细数据返回此函数,将会返回一个列表,列表中会包含字典,字典的键如下::
|
||||
|
||||
{
|
||||
"checkArr": "1", # 是否有权限 1 为有 0为无
|
||||
"create_time": null, # 权限创建
|
||||
"enable": 1, # 权限是否启用
|
||||
"icon": "layui-icon layui-icon-set-fill", # 权限图标
|
||||
"openType": null, # 开启状态
|
||||
"parentId": "0", # 父权限ID
|
||||
"powerId": "1", # 权限ID
|
||||
"powerName": "系统管理", # 权限名称
|
||||
"powerType": "0", # 权限类型
|
||||
"powerUrl": null, # 权限URL
|
||||
"sort": 1, # 权限排序
|
||||
"update_time": null # 权限更新时间
|
||||
}
|
||||
|
||||
|
||||
:param role_filter: dev.role.filter_by() 返回结果。
|
||||
:param detail: 返回详细数据
|
||||
:param p: 如果有多个结果被找到,p可以确定使用第几个结果。内部使用 role_filter.all()[p]
|
||||
:return: 用户拥有的权限列表。
|
||||
"""
|
||||
role = role_filter.all()[p]
|
||||
check_powers = role.power
|
||||
check_powers_list = []
|
||||
for cp in check_powers:
|
||||
check_powers_list.append(cp.id)
|
||||
if not detail:
|
||||
return check_powers_list
|
||||
powers = Power.query.all()
|
||||
power_schema = PowerOutSchema2(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
|
||||
output = power_schema.dump(powers) # 生成可序列化对象
|
||||
for i in output:
|
||||
if int(i.get("powerId")) in check_powers_list:
|
||||
i["checkArr"] = "1"
|
||||
else:
|
||||
i["checkArr"] = "0"
|
||||
return output
|
||||
|
||||
|
||||
def set_power(role_filter, powerIds, p=0):
|
||||
"""
|
||||
保存角色权限。此函数会直接写入数据库。
|
||||
|
||||
:param role_filter: dev.role.filter_by() 返回结果。
|
||||
:param powerIds: 必须是一个包含权限ID的列表。如 [1, 2, 3]
|
||||
:param p: 如果有多个结果被找到,p可以确定使用第几个结果。内部使用 role_filter.all()[p]
|
||||
:return: None
|
||||
"""
|
||||
role = role_filter.all()[p]
|
||||
powers = Power.query.filter(Power.id.in_(powerIds)).all()
|
||||
role.power = powers
|
||||
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def update(role_filter, data):
|
||||
"""
|
||||
更新角色数据。此功能将直接写入数据库。
|
||||
|
||||
可更新的字段如下::
|
||||
|
||||
id name code enable remark details sort create_time update_time power
|
||||
具体参考 applications/models/admin_role.py 中的模型定义。
|
||||
|
||||
参考调用如下::
|
||||
|
||||
role_filter = dev.role.filter_by(id=0) # 获取指定角色ID的角色,注意不要使用会引起歧义的查询条件,否则会匹配到多个角色。
|
||||
dev.role.update(role_filter, {enable: 0}) # 禁用
|
||||
|
||||
:param role_filter: dev.role.filter_by() 返回结果。
|
||||
:param data: 要更新的数据,必须是字典。
|
||||
:return: None
|
||||
"""
|
||||
role_filter.update(data)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def delete(role_filter):
|
||||
"""
|
||||
删除角色。此功能将直接写入数据库。
|
||||
|
||||
:param role_filter: dev.role.filter_by() 返回结果。
|
||||
:return: 是否成功。
|
||||
"""
|
||||
role = role_filter.first()
|
||||
# 删除该角色的权限和用户
|
||||
role.power = []
|
||||
role.user = []
|
||||
|
||||
r = role_filter.delete()
|
||||
db.session.commit()
|
||||
return r
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
集成了对 Pear Admin Flask 的用户操作,并给了相对应的示例。
|
||||
|
||||
调用示例::
|
||||
|
||||
from applications import dev
|
||||
dev.user.login_required # 用户是否登录
|
||||
dev.user.current_user # 当前登录用户
|
||||
dev.user.authorize("XXX", log=True) # 用户是否有此权限
|
||||
|
||||
"""
|
||||
|
||||
from applications.extensions import db
|
||||
from applications.models import Role
|
||||
from applications.models import User
|
||||
|
||||
|
||||
def filter_by(**kwargs):
|
||||
"""
|
||||
用于在用户数据中查询用户信息,可以通过用户名、用户id等进行检索。(建议使用id检索)
|
||||
内部采用的是使用 User.query.filter_by(**kwargs) 进行数据库查询
|
||||
|
||||
注意:此函数返回的结果为构造的 SQL的查询字符串 ,以 user_filter 命名,但是并不是用户数据。
|
||||
|
||||
返回的字段如下::
|
||||
|
||||
id username password_hash create_at update_at
|
||||
enable realname remark avatar dept_id
|
||||
具体参考 applications/models/admin_user.py 中的模型定义。
|
||||
|
||||
|
||||
参考调用如下::
|
||||
|
||||
userinfo = dev.user.filter_by(username='zsq').first() # 查询符合要求的第一个用户
|
||||
print(userinfo.realname) # 获取用户真实名字
|
||||
|
||||
|
||||
:param kwargs: 查询参数
|
||||
:return: 用户数据SQL的查询字符串
|
||||
"""
|
||||
return User.query.filter_by(**kwargs)
|
||||
|
||||
|
||||
def update(user_filter, data):
|
||||
"""
|
||||
更新用户数据,修改将直接保存到数据库中。
|
||||
注意:更新用户角色(role)请使用 dev.user.update_role() 函数。
|
||||
|
||||
可更新的字段如下::
|
||||
|
||||
id username password_hash create_at update_at
|
||||
enable realname remark avatar dept_id
|
||||
具体参考 applications/models/admin_user.py 中的模型定义。
|
||||
|
||||
参考调用如下::
|
||||
|
||||
user_filter = dev.user.filter_by(id=0) # 获取指定用户ID的用户,注意不要使用会引起歧义的查询条件,否则会匹配到多个用户。
|
||||
dev.user.update(user_filter, {username: 'zsq1314'}) # 更新其用户名
|
||||
|
||||
|
||||
|
||||
:param user_filter: dev.user.filter_by() 的结果。
|
||||
:param data: 要更新的数据,必须是字典。
|
||||
:return: None
|
||||
"""
|
||||
user_filter.update(data)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def update_role(user_filter, roleIds):
|
||||
"""
|
||||
更新用户角色,修改将直接保存到数据库中。
|
||||
|
||||
参考调用如下::
|
||||
|
||||
user_filter = dev.user.filter_by(username='zsq') # 获取符合要求的第一个用户
|
||||
roleIds = []
|
||||
roleIds.append(dev.role.filter_by(code='admin').first().id) # 管理员角色ID
|
||||
roleIds.append(dev.role.filter_by(code='common').first().id) # 普通用户角色ID
|
||||
dev.user.update_role(user_filter, roleIds)
|
||||
|
||||
|
||||
:param user_filter: dev.user.filter_by() 的结果。
|
||||
:param roleIds: 要更新的角色ID,作为列表传入。
|
||||
:return: None
|
||||
"""
|
||||
user_filter.first().role = Role.query.filter(Role.id.in_(roleIds)).all()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_role(user_filter):
|
||||
"""
|
||||
获取用户的所有角色ID,将会返回一个整数列表。
|
||||
|
||||
:param user_filter: dev.user.filter_by() 的结果。
|
||||
:return: 列表 (roleIds)
|
||||
"""
|
||||
checked_roles = []
|
||||
for r in user_filter.first().role:
|
||||
checked_roles.append(r.id)
|
||||
return checked_roles
|
||||
|
||||
|
||||
def set_password(user_filter, password):
|
||||
"""
|
||||
设置用户密码,此函数不会验证用户原始密码哈希值,直接写入新密码哈希值。
|
||||
|
||||
参考调用如下::
|
||||
|
||||
user_filter = dev.user.filter_by(username='zsq') # 获取符合要求的用户
|
||||
dev.user.set_password(user_filter, 'zsq1314') # 设置密码
|
||||
|
||||
:param user_filter: dev.user.filter_by() 的结果。
|
||||
:param password: 新密码。
|
||||
:return: None
|
||||
"""
|
||||
user = user_filter.first()
|
||||
user.set_password(password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def add(username, realname, password, roleIds):
|
||||
"""
|
||||
添加一个新用户。函数会判断用户名是否已存在,存在返回 False ,成功返回用户数据(userinfo)。此函数会直接写入数据库。
|
||||
注意:此函数创建出来的用户默认是禁用的,可以使用 enable 启用。
|
||||
|
||||
:param username: 新用户名
|
||||
:param realname: 真实名字
|
||||
:param password: 密码字符串
|
||||
:param roleIds: 角色id列表,如 [1, 2],角色id具体查看 dev.role.get_all() 函数。
|
||||
:return: 是否成功
|
||||
"""
|
||||
if bool(User.query.filter_by(username=username).count()):
|
||||
return False
|
||||
user = User(username=username, realname=realname)
|
||||
user.set_password(password)
|
||||
db.session.add(user)
|
||||
roles = Role.query.filter(Role.id.in_(roleIds)).all()
|
||||
for r in roles:
|
||||
user.role.append(r)
|
||||
db.session.commit()
|
||||
return user
|
||||
|
||||
|
||||
def delete(user_filter):
|
||||
"""
|
||||
删除一个用户。此函数立刻写入数据库。
|
||||
|
||||
:param user_filter: dev.user.filter_by() 的结果。
|
||||
:return: 是否成功
|
||||
"""
|
||||
user = user_filter.first()
|
||||
user.role = []
|
||||
res = user_filter.delete()
|
||||
db.session.commit()
|
||||
return res
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from applications.view.index import register_index_views
|
||||
from applications.view.passport import register_passport_views
|
||||
from applications.view.rights import register_rights_view
|
||||
from applications.view.department import register_dept_views
|
||||
|
||||
from applications.view.plugin import register_plugin_views
|
||||
|
||||
def init_view(app):
|
||||
register_admin_views(app)
|
||||
@@ -11,3 +11,4 @@ def init_view(app):
|
||||
register_rights_view(app)
|
||||
register_passport_views(app)
|
||||
register_dept_views(app)
|
||||
register_plugin_views(app)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
from datetime import datetime
|
||||
import sys
|
||||
import time
|
||||
import psutil
|
||||
import platform
|
||||
from datetime import datetime
|
||||
from flask import Blueprint, render_template, jsonify
|
||||
from applications.common.utils.rights import authorize
|
||||
|
||||
@@ -22,8 +23,8 @@ def main():
|
||||
python_version = platform.python_version()
|
||||
# 逻辑cpu数量
|
||||
cpu_count = psutil.cpu_count()
|
||||
# cup使用率
|
||||
cpus_percent = psutil.cpu_percent(interval=0.1)
|
||||
# cpu使用率
|
||||
cpus_percent = psutil.cpu_percent(interval=0.1, percpu=False) # percpu 获取主使用率
|
||||
# 内存
|
||||
memory_information = psutil.virtual_memory()
|
||||
# 内存使用率
|
||||
@@ -79,10 +80,19 @@ def main():
|
||||
@admin_monitor_bp.get('/polling')
|
||||
@authorize("admin:monitor:main")
|
||||
def ajax_polling():
|
||||
# 获取cup使用率
|
||||
cpus_percent = psutil.cpu_percent(interval=0.1)
|
||||
# 获取cpu使用率
|
||||
cpus_percent = psutil.cpu_percent(interval=0.1, percpu=False) # percpu 获取主使用率
|
||||
# 获取内存使用率
|
||||
memory_information = psutil.virtual_memory()
|
||||
memory_usage = memory_information.percent
|
||||
time_now = time.strftime('%H:%M:%S ', time.localtime(time.time()))
|
||||
return jsonify(cups_percent=cpus_percent, memory_used=memory_usage, time_now=time_now)
|
||||
|
||||
# 关闭程序
|
||||
@admin_monitor_bp.get('/kill')
|
||||
@authorize("admin:monitor:main")
|
||||
def kill():
|
||||
for proc in psutil.process_iter():
|
||||
if proc.pid == os.getpid():
|
||||
proc.kill()
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,169 @@
|
||||
import shutil
|
||||
|
||||
from flask import Flask
|
||||
from flask import Blueprint, render_template, request, jsonify, escape
|
||||
from applications.common.utils.http import table_api, fail_api, success_api
|
||||
import os
|
||||
import json
|
||||
import traceback
|
||||
import importlib
|
||||
|
||||
import applications.dev
|
||||
from applications.common.utils.rights import authorize
|
||||
|
||||
plugin_bp = Blueprint('plugin', __name__, url_prefix='/plugin')
|
||||
PLUGIN_ENABLE_FOLDERS = []
|
||||
|
||||
def register_plugin_views(app: Flask):
|
||||
global PLUGIN_ENABLE_FOLDERS
|
||||
applications.dev.app = app # 对app重新赋值 便于插件简单调用
|
||||
app.register_blueprint(plugin_bp)
|
||||
# 载入插件过程
|
||||
# plugin_folder 配置的是插件的文件夹名
|
||||
PLUGIN_ENABLE_FOLDERS = json.loads(app.config['PLUGIN_ENABLE_FOLDERS'])
|
||||
for plugin_folder in PLUGIN_ENABLE_FOLDERS:
|
||||
plugin_info = {}
|
||||
try:
|
||||
with open("plugins/" + plugin_folder + "/__init__.json", "r", encoding='utf-8') as f:
|
||||
plugin_info = json.loads(f.read())
|
||||
print(f"* Plugin: Loading {plugin_info['plugin_name']} ......")
|
||||
# 初始化完成事件
|
||||
try:
|
||||
getattr(importlib.import_module('plugins.' + plugin_folder), "event_init")(app)
|
||||
except AttributeError: # 没有插件启用事件就不调用
|
||||
pass
|
||||
except BaseException as error:
|
||||
return fail_api(msg="Crash a error! Info: " + str(error))
|
||||
print(f"* Plugin: Loaded {plugin_info['plugin_name']} !")
|
||||
except BaseException as e:
|
||||
info = f"* Plugin: Crash a error when loading {plugin_info['plugin_name'] if len(plugin_info) != 0 else 'plugin'} :" + "\n"
|
||||
info += 'str(Exception):\t' + str(Exception) + "\n"
|
||||
info += 'str(e):\t\t' + str(e) + "\n"
|
||||
info += 'repr(e):\t' + repr(e) + "\n"
|
||||
info += 'traceback.format_exc():\n%s' + traceback.format_exc()
|
||||
print(info)
|
||||
|
||||
|
||||
@plugin_bp.get('/')
|
||||
@authorize("admin:plugin:main", log=True)
|
||||
def main():
|
||||
"""此处渲染管理模板"""
|
||||
return render_template('admin/plugin/main.html')
|
||||
|
||||
|
||||
@plugin_bp.get('/data')
|
||||
@authorize("admin:plugin:main", log=True)
|
||||
def data():
|
||||
"""请求插件数据"""
|
||||
plugin_name = escape(request.args.get("plugin_name"))
|
||||
all_plugins = []
|
||||
count = 0
|
||||
for filename in os.listdir("plugins"):
|
||||
try:
|
||||
with open("plugins/" + filename + "/__init__.json", "r", encoding='utf-8') as f:
|
||||
info = json.loads(f.read())
|
||||
|
||||
if plugin_name is None:
|
||||
if info['plugin_name'].find(plugin_name) == -1:
|
||||
continue
|
||||
|
||||
all_plugins.append(
|
||||
{
|
||||
"plugin_name": info["plugin_name"],
|
||||
"plugin_version": info["plugin_version"],
|
||||
"plugin_description": info["plugin_description"],
|
||||
"developer_name": info["developer_name"],
|
||||
"developer_website": info["developer_website"],
|
||||
"developer_email": info["developer_email"],
|
||||
"developer_phone": info["developer_phone"],
|
||||
"plugin_folder_name": filename,
|
||||
"enable": "1" if filename in PLUGIN_ENABLE_FOLDERS else "0"
|
||||
}
|
||||
)
|
||||
count += 1
|
||||
except BaseException as error:
|
||||
print(filename, error)
|
||||
continue
|
||||
return table_api(data=all_plugins, count=count)
|
||||
|
||||
|
||||
@plugin_bp.put('/enable')
|
||||
@authorize("admin:plugin:enable", log=True)
|
||||
def enable():
|
||||
"""启用插件"""
|
||||
plugin_folder_name = request.json.get('plugin_folder_name')
|
||||
if plugin_folder_name:
|
||||
try:
|
||||
if plugin_folder_name not in PLUGIN_ENABLE_FOLDERS:
|
||||
PLUGIN_ENABLE_FOLDERS.append(plugin_folder_name)
|
||||
with open(".flaskenv", "r", encoding='utf-8') as f:
|
||||
flaskenv = f.read() # type: str
|
||||
pos1 = flaskenv.find("PLUGIN_ENABLE_FOLDERS")
|
||||
pos2 = flaskenv.find("\n", pos1)
|
||||
with open(".flaskenv", "w", encoding='utf-8') as f:
|
||||
if pos2 == -1:
|
||||
f.write(flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS))
|
||||
else:
|
||||
f.write(
|
||||
flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS) + flaskenv[
|
||||
pos2:])
|
||||
# 启用插件事件
|
||||
try:
|
||||
getattr(importlib.import_module('plugins.' + plugin_folder_name), "event_enable")()
|
||||
except AttributeError: # 没有插件启用事件就不调用
|
||||
pass
|
||||
except BaseException as error:
|
||||
return fail_api(msg="Crash a error! Info: " + str(error))
|
||||
|
||||
except BaseException as error:
|
||||
return fail_api(msg="Crash a error! Info: " + str(error))
|
||||
return success_api(msg="启用成功,要使修改生效需要重启程序。")
|
||||
return fail_api(msg="数据错误")
|
||||
|
||||
|
||||
@plugin_bp.put('/disable')
|
||||
@authorize("admin:plugin:enable", log=True)
|
||||
def disable():
|
||||
"""禁用插件"""
|
||||
plugin_folder_name = request.json.get('plugin_folder_name')
|
||||
if plugin_folder_name:
|
||||
try:
|
||||
if plugin_folder_name in PLUGIN_ENABLE_FOLDERS:
|
||||
PLUGIN_ENABLE_FOLDERS.remove(plugin_folder_name)
|
||||
with open(".flaskenv", "r", encoding='utf-8') as f:
|
||||
flaskenv = f.read() # type: str
|
||||
pos1 = flaskenv.find("PLUGIN_ENABLE_FOLDERS")
|
||||
pos2 = flaskenv.find("\n", pos1)
|
||||
with open(".flaskenv", "w", encoding='utf-8') as f:
|
||||
if pos2 == -1:
|
||||
f.write(flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS))
|
||||
else:
|
||||
f.write(
|
||||
flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS) + flaskenv[
|
||||
pos2:])
|
||||
|
||||
# 禁用插件事件
|
||||
try:
|
||||
getattr(importlib.import_module('plugins.' + plugin_folder_name), "event_disable")()
|
||||
except AttributeError: # 没有插件禁用事件就不调用
|
||||
pass
|
||||
except BaseException as error:
|
||||
return fail_api(msg="Crash a error! Info: " + str(error))
|
||||
|
||||
except BaseException as error:
|
||||
return fail_api(msg="Crash a error! Info: " + str(error))
|
||||
return success_api(msg="禁用成功,要使修改生效需要重启程序。")
|
||||
return fail_api(msg="数据错误")
|
||||
|
||||
|
||||
# 删除
|
||||
@plugin_bp.delete('/remove/<string:plugin_folder_name>')
|
||||
@authorize("admin:mail:remove", log=True)
|
||||
def delete(plugin_folder_name):
|
||||
if plugin_folder_name in PLUGIN_ENABLE_FOLDERS:
|
||||
return fail_api(msg="您必须先禁用插件!")
|
||||
try:
|
||||
shutil.rmtree(os.path.abspath("plugins/" + plugin_folder_name))
|
||||
return success_api(msg="删除成功")
|
||||
except BaseException as error:
|
||||
return fail_api(msg="删除失败!原因:" + str(error))
|
||||
Reference in New Issue
Block a user