更新插件功能

This commit is contained in:
wojiaoyishang
2023-02-10 21:29:39 +08:00
parent 51db1944ed
commit 7bef64f632
24 changed files with 1442 additions and 137 deletions
+15
View File
@@ -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"
+9
View File
@@ -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
+89
View File
@@ -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)
+108
View File
@@ -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: 父公司ID0未总公司
: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
+83
View File
@@ -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
+138
View File
@@ -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: 父ID0为顶级菜单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
+157
View File
@@ -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
+160
View File
@@ -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
+2 -1
View File
@@ -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)
+16 -6
View File
@@ -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)
+169
View File
@@ -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))