diff --git a/.flaskenv b/.flaskenv deleted file mode 100644 index c6b022c..0000000 --- a/.flaskenv +++ /dev/null @@ -1,32 +0,0 @@ -# flask配置 -FLASK_APP = app.py -FLASK_ENV = development -FLASK_DEBUG = 1 -FLASK_RUN_HOST = 127.0.0.1 -FLASK_RUN_PORT = 5000 - -# pear admin flask配置 -SYSTEM_NAME = Pear Admin - -# MySql配置信息 -MYSQL_HOST = 127.0.0.1 -# MYSQL_HOST = dbserver -MYSQL_PORT = 3306 -MYSQL_DATABASE = PearAdminFlask -MYSQL_USERNAME = root -MYSQL_PASSWORD = root - -# Redis 配置 -# REDIS_HOST=127.0.0.1 -# REDIS_PORT=6379 - -# 密钥配置(记得改) -SECRET_KEY = 'pear-admin-flask' - -# 邮箱配置 -MAIL_SERVER = 'smtp.qq.com' -MAIL_USERNAME = '123@qq.com' -MAIL_PASSWORD = 'XXXXX' # 生成的授权码 - -# 插件配置 -PLUGIN_ENABLE_FOLDERS = ["helloworld"] \ No newline at end of file diff --git a/.gitignore b/.gitignore index 935a2f8..aabeb25 100644 --- a/.gitignore +++ b/.gitignore @@ -115,4 +115,6 @@ dmypy.json .pyre/ #ide -.idea/ \ No newline at end of file +.idea/ +## 迁移文件 +#migrations/ \ No newline at end of file diff --git a/app.py b/app.py index a8a7d48..d7cfec3 100644 --- a/app.py +++ b/app.py @@ -1,10 +1,7 @@ from applications import create_app -from flask_migrate import Migrate -from applications.extensions import db app = create_app() -migrate = Migrate(app, db) if __name__ == '__main__': app.run() diff --git a/applications/__init__.py b/applications/__init__.py index e24950e..bf785aa 100644 --- a/applications/__init__.py +++ b/applications/__init__.py @@ -1,7 +1,7 @@ import os from flask import Flask -from applications.common.script import init_script +from applications.common.script import admin_cli from applications.extensions import init_plugs from applications.view import init_view from applications.configs import config @@ -24,7 +24,7 @@ def create_app(config_name=None): init_view(app) # 注册命令 - init_script(app) + app.cli.add_command(admin_cli) if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': logo() diff --git a/applications/common/admin.py b/applications/common/admin.py index 3d66369..55f8db5 100644 --- a/applications/common/admin.py +++ b/applications/common/admin.py @@ -1,49 +1,8 @@ -import copy -from collections import OrderedDict from io import BytesIO -from flask import session, make_response, current_app -from flask_login import current_user + +from flask import session, make_response from applications.common.utils.gen_captcha import vieCode -from applications.schemas import PowerOutSchema - - - -# 生成菜单树 -def make_menu_tree(): - role = current_user.role - powers = [] - for i in role: - # 如果角色没有被启用就直接跳过 - if i.enable == 0: - continue - # 变量角色用户的权限 - for p in i.power: - # 如果权限关闭了就直接跳过 - if p.enable == 0: - continue - # 一二级菜单 - if int(p.type) in [0, 1] and p not in powers: - powers.append(p) - - power_schema = PowerOutSchema(many=True) # 用已继承 ma.ModelSchema 类的自定制类生成序列化类 - power_dict = power_schema.dump(powers) # 生成可序列化对象 - power_dict.sort(key=lambda x: (x['parent_id'], x['id']), reverse=True) - - menu_dict = OrderedDict() - for _dict in power_dict: - if _dict['id'] in menu_dict: - # 当前节点添加子节点 - _dict['children'] = copy.deepcopy(menu_dict[_dict['id']]) - _dict['children'].sort(key=lambda item: item['sort']) - # 删除子节点 - del menu_dict[_dict['id']] - - if _dict['parent_id'] not in menu_dict: - menu_dict[_dict['parent_id']] = [_dict] - else: - menu_dict[_dict['parent_id']].append(_dict) - return sorted(menu_dict.get(0), key=lambda item: item['sort']) # 生成验证码 @@ -56,78 +15,4 @@ def get_captcha(): out.seek(0) resp = make_response(out.read()) resp.content_type = 'image/png' - return resp, code - - -def get_render_config(): - # 网站配置 - config = dict(logo={ - # 网站名称 - "title": current_app.config.get("SYSTEM_NAME"), - # 网站图标 - "image": "/static/admin/admin/images/logo.png" - # 菜单配置 - }, menu={ - # 菜单数据来源 - "data": "/rights/menu", - "collaspe": False, - # 是否同时只打开一个菜单目录 - "accordion": True, - "method": "GET", - # 是否开启多系统菜单模式 - "control": False, - # 顶部菜单宽度 PX - "controlWidth": 500, - # 默认选中的菜单项 - "select": "0", - # 是否开启异步菜单,false 时 data 属性设置为菜单数据,false 时为 json 文件或后端接口 - "async": True - }, tab={ - # 是否开启多选项卡 - "enable": True, - # 切换选项卡时,是否刷新页面状态 - "keepState": True, - # 是否开启 Tab 记忆 - "session": True, - # 最大可打开的选项卡数量 - "max": 30, - "index": { - # 标识 ID , 建议与菜单项中的 ID 一致 - "id": "10", - # 页面地址 - "href": "/admin/welcome", - # 标题 - "title": "首页" - } - }, theme={ - # 默认主题色,对应 colors 配置中的 ID 标识 - "defaultColor": "2", - # 默认的菜单主题 dark-theme 黑 / light-theme 白 - "defaultMenu": "dark-theme", - # 是否允许用户切换主题,false 时关闭自定义主题面板 - "allowCustom": True - }, colors=[{ - "id": "1", - "color": "#2d8cf0" - }, - { - "id": "2", - "color": "#5FB878" - }, - { - "id": "3", - "color": "#1E9FFF" - }, { - "id": "4", - "color": "#FFB800" - }, { - "id": "5", - "color": "darkgray" - } - ], links=current_app.config.get("SYSTEM_PANEL_LINKS"), other={ - # 主页动画时长 - "keepLoad": 1200, - # 布局顶部主题 - "autoHead": False - }, header=False) - return config + return resp, code \ No newline at end of file diff --git a/applications/common/script/__init__.py b/applications/common/script/__init__.py index f475c86..b8d290c 100644 --- a/applications/common/script/__init__.py +++ b/applications/common/script/__init__.py @@ -1,26 +1,3 @@ -import os - -import click - -from applications.common.script.initdb import init_db -from applications.common.script.newmodular.new import NewViewModular - - -def init_script(app): - @app.cli.command() - def init(): - init_db() - - @app.cli.command() - @click.option('--type', prompt="请输入类型", help='新增的类型') - @click.option('--name', prompt="请输入新增的名称", help='新增的名称') - def new(type,name): - if type == 'view': - if name.count('/') > 1: - print("目前只支持二级目录,多级目录需要蓝图嵌套,本命令暂不支持,请手动创建") - quit() - if type == "view" and os.path.exists(f"applications/view/{name}.py"): - print(f'已经存在视图模块{name}.py') - quit() - NewViewModular(name=name).new_view() +from flask.cli import AppGroup +admin_cli = AppGroup("admin") diff --git a/applications/common/script/init_db.py b/applications/common/script/init_db.py new file mode 100644 index 0000000..7831c3e --- /dev/null +++ b/applications/common/script/init_db.py @@ -0,0 +1,689 @@ +import datetime + +from . import admin_cli +from applications.extensions import db +from applications.models import User, Role, Dept, Power + +now_time = datetime.datetime.now() +userdata = [ + User( + id=1, + username='admin', + password_hash='pbkdf2:sha256:150000$raM7mDSr$58fe069c3eac01531fc8af85e6fc200655dd2588090530084d182e6ec9d52c85', + create_at=now_time, + enable=1, + realname='超级管理', + remark='要是不能把握时机,就要终身蹭蹬,一事无成!', + avatar='http://127.0.0.1:5000/_uploads/photos/1617291580000.jpg', + dept_id=1, + ), + User( + id=2, + username='test', + password_hash='pbkdf2:sha256:150000$cRS8bYNh$adb57e64d929863cf159f924f74d0634f1fecc46dba749f1bfaca03da6d2e3ac', + create_at=now_time, + enable=1, + realname='测试', + remark='要是不能把握时机,就要终身蹭蹬,一事无成!', + avatar='http://127.0.0.1:5000/_uploads/photos/1617291580000.jpg', + dept_id=1, + ), + User( + id='3', + username='wind', + password_hash='pbkdf2:sha256:150000$skME1obT$6a2c20cd29f89d7d2f21d9e373a7e3445f70ebce3ef1c3a555e42a7d17170b37', + create_at=now_time, + enable=1, + realname='风', + remark='要是不能把握时机,就要终身蹭蹬,一事无成!', + avatar='http://127.0.0.1:5000/_uploads/photos/1617291580000.jpg', + dept_id=7, + ), +] +roledata = [ + Role( + id=1, + code='admin', + name='管理员', + enable=1, + details='管理员', + sort=1, + create_time=now_time, + ), + Role( + id=2, + code='common', + name='普通用户', + enable=1, + details='只有查看,没有增删改权限', + sort=2, + create_time=now_time, + ) +] +deptdata = [ + Dept( + id=1, + parent_id=0, + dept_name='总公司', + sort=1, + leader='就眠仪式', + phone='12312345679', + email='123qq.com', + status=1, + remark='这是总公司', + create_at=now_time + ), + Dept( + id=4, + parent_id=1, + dept_name='济南分公司', + sort=2, + leader='就眠仪式', + phone='12312345679', + email='123qq.com', + status=1, + remark='这是济南', + create_at=now_time + + ), + Dept( + id=5, + parent_id=1, + dept_name='唐山分公司', + sort=4, + leader='mkg', + phone='12312345679', + email='123qq.com', + status=1, + remark='这是唐山', + create_at=now_time + + ), + Dept( + id=7, + parent_id=4, + dept_name='济南分公司开发部', + sort=5, + leader='就眠仪式', + phone='12312345679', + email='123qq.com', + status=1, + remark='测试', + create_at=now_time + + ), + Dept( + id=8, + parent_id=5, + dept_name='唐山测试部', + sort=5, + leader='mkg', + phone='12312345679', + email='123qq.com', + status=1, + remark='测试部', + create_at=now_time + + ) +] +powerdata = [ + Power( + id=1, + name='系统管理', + type='0', + code='', + url=None, + open_type=None, + parent_id='0', + icon='layui-icon layui-icon-set-fill', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=3, + name='用户管理', + type='1', + code='admin:user:main', + url='/admin/user/', + open_type='_iframe', + parent_id='1', + icon='layui-icon layui-icon layui-icon layui-icon layui-icon-rate', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=4, + name='权限管理', + type='1', + code='admin:power:main', + url='/admin/power/', + open_type='_iframe', + parent_id='1', + icon=None, + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=9, + name='角色管理', + type='1', + code='admin:role:main', + url='/admin/role', + open_type='_iframe', + parent_id='1', + icon='layui-icon layui-icon-username', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=12, + name='系统监控', + type='1', + code='admin:monitor:main', + url='/admin/monitor', + open_type='_iframe', + parent_id='1', + icon='layui-icon layui-icon-vercode', + sort=5, + create_time=now_time, + enable=1, + + ), Power( + id=13, + name='日志管理', + type='1', + code='admin:log:main', + url='/admin/log', + open_type='_iframe', + parent_id='1', + icon='layui-icon layui-icon-read', + sort=4, + create_time=now_time, + enable=1, + + ), Power( + id=17, + name='文件管理', + type='0', + code='', + url='', + open_type='', + parent_id='0', + icon='layui-icon layui-icon-camera', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=18, + name='图片上传', + type='1', + code='admin:file:main', + url='/admin/file', + open_type='_iframe', + parent_id='17', + icon='layui-icon layui-icon-camera', + sort=5, + create_time=now_time, + enable=1, + + ), Power( + id=21, + name='权限增加', + type='2', + code='admin:power:add', + url='', + open_type='', + parent_id='4', + icon='layui-icon layui-icon-add-circle', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=22, + name='用户增加', + type='2', + code='admin:user:add', + url='', + open_type='', + parent_id='3', + icon='layui-icon layui-icon-add-circle', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=23, + name='用户编辑', + type='2', + code='admin:user:edit', + url='', + open_type='', + parent_id='3', + icon='layui-icon layui-icon-rate', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=24, + name='用户删除', + type='2', + code='admin:user:remove', + url='', + open_type='', + parent_id='3', + icon='', + sort=3, + create_time=now_time, + enable=1, + + ), Power( + id=25, + name='权限编辑', + type='2', + code='admin:power:edit', + url='', + open_type='', + parent_id='4', + icon='', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=26, + name='用户删除', + type='2', + code='admin:power:remove', + url='', + open_type='', + parent_id='4', + icon='', + sort=3, + create_time=now_time, + enable=1, + + ), Power( + id=27, + name='用户增加', + type='2', + code='admin:role:add', + url='', + open_type='', + parent_id='9', + icon='', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=28, + name='角色编辑', + type='2', + code='admin:role:edit', + url='', + open_type='', + parent_id='9', + icon='', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=29, + name='角色删除', + type='2', + code='admin:role:remove', + url='', + open_type='', + parent_id='9', + icon='', + sort=3, + create_time=now_time, + enable=1, + + ), Power( + id=30, + name='角色授权', + type='2', + code='admin:role:power', + url='', + open_type='', + parent_id='9', + icon='', + sort=4, + create_time=now_time, + enable=1, + + ), Power( + id=31, + name='图片增加', + type='2', + code='admin:file:add', + url='', + open_type='', + parent_id='18', + icon='', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=32, + name='图片删除', + type='2', + code='admin:file:delete', + url='', + open_type='', + parent_id='18', + icon='', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=44, + name='数据字典', + type='1', + code='admin:dict:main', + url='/admin/dict', + open_type='_iframe', + parent_id='1', + icon='layui-icon layui-icon-console', + sort=6, + create_time=now_time, + enable=1, + + ), Power( + id=45, + name='字典增加', + type='2', + code='admin:dict:add', + url='', + open_type='', + parent_id='44', + icon='', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=46, + name='字典修改', + type='2', + code='admin:dict:edit', + url='', + open_type='', + parent_id='44', + icon='', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=47, + name='字典删除', + type='2', + code='admin:dict:remove', + url='', + open_type='', + parent_id='44', + icon='', + sort=3, + create_time=now_time, + enable=1, + + ), Power( + id=48, + name='部门管理', + type='1', + code='admin:dept:main', + url='/dept', + open_type='_iframe', + parent_id='1', + icon='layui-icon layui-icon-group', + sort=3, + create_time=now_time, + enable=1, + + ), Power( + id=49, + name='部门增加', + type='2', + code='admin:dept:add', + url='', + open_type='', + parent_id='48', + icon='', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=50, + name='部门编辑', + type='2', + code='admin:dept:edit', + url='', + open_type='', + parent_id='48', + icon='', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=51, + name='部门删除', + type='2', + code='admin:dept:remove', + url='', + open_type='', + parent_id='48', + icon='', + sort=3, + create_time=now_time, + enable=1, + + ), Power( + id=52, + name='定时任务', + type='0', + code='', + url='', + open_type='', + parent_id='0', + icon='layui-icon layui-icon-log', + sort=3, + create_time=now_time, + enable=1, + + ), Power( + id=53, + name='任务管理', + type='1', + code='admin:task:main', + url='/admin/task', + open_type='_iframe', + parent_id='52', + icon='layui-icon ', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=54, + name='任务增加', + type='2', + code='admin:task:add', + url='', + open_type='', + parent_id='53', + icon='layui-icon ', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=55, + name='任务修改', + type='2', + code='admin:task:edit', + url='', + open_type='', + parent_id='53', + icon='layui-icon ', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=56, + name='任务删除', + type='2', + code='admin:task:remove', + url='', + open_type='', + parent_id='53', + icon='layui-icon ', + sort=23, + create_time=now_time, + enable=1, + + ), Power( + id=57, + name='邮件管理', + type='1', + code='admin:mail:main', + url='/admin/mail', + open_type='_iframe', + parent_id='1', + icon='layui-icon ', + sort=7, + create_time=now_time, + enable=1, + + ), Power( + id=58, + name='邮件发送', + type='2', + code='admin:mail:add', + url='', + open_type='', + parent_id='57', + icon='layui-icon layui-icon-ok-circle', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=59, + name='邮件删除', + type='2', + code='admin:mail:remove', + url='', + open_type='', + parent_id='57', + icon='', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=60, + name='拓展插件', + type='0', + code='', + url='', + open_type='', + parent_id='0', + icon='layui-icon layui-icon-senior', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=61, + name='插件管理', + type='1', + code='admin:plugin:main', + url='/plugin', + open_type='_iframe', + parent_id='60', + icon='layui-icon layui-icon', + sort=2, + create_time=now_time, + enable=1, + + ), Power( + id=62, + name='启禁插件', + type='2', + code='admin:plugin:enable', + url='', + open_type='', + parent_id='61', + icon='layui-icon layui-icon', + sort=1, + create_time=now_time, + enable=1, + + ), Power( + id=63, + name='删除插件', + type='2', + code='admin:plugin:remove', + url='', + open_type='', + parent_id='61', + icon='layui-icon layui-icon', + sort=2, + create_time=now_time, + enable=1, + + ) + +] + + +def add_user_role(): + admin_role = Role.query.filter_by(id=1).first() + admin_user = User.query.filter_by(id=1).first() + admin_user.role.append(admin_role) + test_role = Role.query.filter_by(id=2).first() + test_user = User.query.filter_by(id=2).first() + test_user.role.append(test_role) + db.session.commit() + + +def add_role_power(): + admin_powers = Power.query.filter(Power.id.in_([1, 3, 4, 9, 12, 13, 17, 18, 44, 48])).all() + admin_user = Role.query.filter_by(id=2).first() + for i in admin_powers: + admin_user.power.append(i) + db.session.commit() + + +@admin_cli.command("init") +def init_db(): + db.session.add_all(userdata) + print("加载系统必须用户数据") + db.session.add_all(roledata) + print("加载系统必须角色数据") + db.session.add_all(deptdata) + print("加载系统必须部门数据") + db.session.add_all(powerdata) + print("加载系统必须权限数据") + db.session.commit() + print("基础数据存入") + add_user_role() + print("用户角色数据存入") + add_role_power() + print("角色权限数据存入") + print("数据初始化完成,请使用flask run命令运行") diff --git a/applications/common/script/initdb.py b/applications/common/script/initdb.py deleted file mode 100644 index 51f5f57..0000000 --- a/applications/common/script/initdb.py +++ /dev/null @@ -1,60 +0,0 @@ -from dotenv import dotenv_values -import sqlparse -import pymysql -config = dotenv_values('.flaskenv') -# MySql配置信息 -HOST = config.get('MYSQL_HOST') or '127.0.0.1' -PORT = config.get('MYSQL_PORT') or 3306 -DATABASE = config.get('MYSQL_DATABASE') or 'PearAdminFlask' -USERNAME = config.get('MYSQL_USERNAME') or 'root' -PASSWORD = config.get('MYSQL_PASSWORD') or '123456' - - -def is_exist_database(): - db = pymysql.connect(host=HOST, port=int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4') - cursor1 = db.cursor() - sql = "select * from information_schema.SCHEMATA WHERE SCHEMA_NAME = '%s' ; " % DATABASE - res = cursor1.execute(sql) - db.close() - return res - - -def init_database(): - db = pymysql.connect(host=HOST, port=int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4') - cursor1 = db.cursor() - sql = "CREATE DATABASE IF NOT EXISTS %s CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" % DATABASE - res = cursor1.execute(sql) - db.close() - return res - - -def execute_fromfile(filename): - db = pymysql.connect(host=HOST, port=int(PORT), user=USERNAME, password=PASSWORD, database=DATABASE, - charset='utf8mb4') - fd = open(filename, 'r', encoding='utf-8') - cursor = db.cursor() - sqlfile = fd.read() - sqlfile = sqlparse.format(sqlfile, strip_comments=True).strip() - - sqlcommamds = sqlfile.split(';') - - for command in sqlcommamds: - try: - cursor.execute(command) - db.commit() - - except Exception as msg: - - db.rollback() - db.close() - - -def init_db(): - if is_exist_database(): - print('数据库已经存在,为防止误初始化,请手动删除 %s 数据库' % str(DATABASE)) - return - if init_database(): - print('数据库%s创建成功' % str(DATABASE)) - execute_fromfile('pear.sql') - print('表创建成功') - print('欢迎使用pear-admin-flask,请使用 flask run 命令启动程序') diff --git a/applications/common/script/new_modular.py b/applications/common/script/new_modular.py new file mode 100644 index 0000000..29263a5 --- /dev/null +++ b/applications/common/script/new_modular.py @@ -0,0 +1,18 @@ +import click + +from applications import admin_cli +from applications.common.script.newmodular.new import NewViewModular + + +@admin_cli.command() +@click.option('--type', prompt="请输入类型", help='新增的类型') +@click.option('--name', prompt="请输入新增的名称", help='新增的名称') +def new(type, name): + if type == 'view': + if name.count('/') > 1: + print("目前只支持二级目录,多级目录需要蓝图嵌套,本命令暂不支持,请手动创建") + quit() + if type == "view" and os.path.exists(f"applications/view/{name}.py"): + print(f'已经存在视图模块{name}.py') + quit() + NewViewModular(name=name).new_view() diff --git a/applications/common/utils/rights.py b/applications/common/utils/rights.py index 5ae9e31..edd857f 100644 --- a/applications/common/utils/rights.py +++ b/applications/common/utils/rights.py @@ -1,6 +1,6 @@ from functools import wraps -from flask import abort, request, jsonify, session -from flask_login import login_required +from flask import abort, request, jsonify, session, current_app +from flask_login import login_required, current_user from applications.common.admin_log import admin_log @@ -17,7 +17,7 @@ def authorize(power: str, log: bool = False): @wraps(func) def wrapper(*args, **kwargs): # 定义管理员的id为1 - if 1 in session.get('role')[0]: + if current_user.username == current_app.config.get("SUPERADMIN"): return func(*args, **kwargs) if not power in session.get('permissions'): if log: diff --git a/applications/configs/config.py b/applications/configs/config.py index f3e2cb7..65b683d 100644 --- a/applications/configs/config.py +++ b/applications/configs/config.py @@ -5,22 +5,15 @@ 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: + DEBUG = True + HOST = '127.0.0.1' + PORT = 5000 - SYSTEM_NAME = os.getenv('SYSTEM_NAME', 'Pear Admin') + SUPERADMIN = 'admin' + + SYSTEM_NAME = 'Pear Admin' # 主题面板的链接列表配置 SYSTEM_PANEL_LINKS = [ { @@ -47,18 +40,18 @@ class BaseConfig: # JSON配置 JSON_AS_ASCII = False - SECRET_KEY = os.getenv('SECRET_KEY', 'dev key') + SECRET_KEY = "pear-admin-flask" # redis配置 - REDIS_HOST = os.getenv('REDIS_HOST') or "127.0.0.1" - REDIS_PORT = int(os.getenv('REDIS_PORT') or 6379) + REDIS_HOST = "127.0.0.1" + REDIS_PORT = 6379 # mysql 配置 - MYSQL_USERNAME = os.getenv('MYSQL_USERNAME') or "root" - MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD') or "123456" - MYSQL_HOST = os.getenv('MYSQL_HOST') or "127.0.0.1" - MYSQL_PORT = int(os.getenv('MYSQL_PORT') or 3306) - MYSQL_DATABASE = os.getenv('MYSQL_DATABASE') or "PearAdminFlask" + MYSQL_USERNAME = "root" + MYSQL_PASSWORD = "123456" + MYSQL_HOST = "127.0.0.1" + MYSQL_PORT = 3306 + MYSQL_DATABASE = "PearAdminFlask1" # mysql 数据库的配置信息 SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MYSQL_USERNAME}:{urlquote(MYSQL_PASSWORD)}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}?charset=utf8mb4" @@ -66,19 +59,19 @@ class BaseConfig: # 默认日志等级 LOG_LEVEL = logging.WARN # - MAIL_SERVER = os.getenv('MAIL_SERVER') or 'smtp.qq.com' + MAIL_SERVER = 'smtp.qq.com' MAIL_USE_TLS = False MAIL_USE_SSL = True MAIL_PORT = 465 - MAIL_USERNAME = os.getenv('MAIL_USERNAME') or '123@qq.com' - MAIL_PASSWORD = os.getenv('MAIL_PASSWORD') or 'XXXXX' # 生成的授权码 - # 默认发件人的邮箱,这里填写和MAIL_USERNAME一致即可 - MAIL_DEFAULT_SENDER = ('pear admin', os.getenv('MAIL_USERNAME') or '123@qq.com') + MAIL_USERNAME = '123@qq.com' + MAIL_PASSWORD = 'XXXXX' # 生成的授权码 + MAIL_DEFAULT_SENDER = MAIL_USERNAME # 設置 APSCHEDULER 參數 - SCHEDULER_API_ENABLED = os.getenv('SCHEDULER_API_ENABLED') or False + SCHEDULER_API_ENABLED = False SCHEDULER_JOBSTORES: dict = { - 'default': SQLAlchemyJobStore(url=f'mysql+pymysql://{MYSQL_USERNAME}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}') + 'default': SQLAlchemyJobStore( + url=f'mysql+pymysql://{MYSQL_USERNAME}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}') } SCHEDULER_EXECUTORS: dict = { 'default': ThreadPoolExecutor(20) @@ -87,11 +80,11 @@ class BaseConfig: 'coalesce': False, 'max_instances': 3 } - + # 插件配置 - PLUGIN_ENABLE_FOLDERS = os.getenv('PLUGIN_ENABLE_FOLDERS') - - # 配置多个数据库连接的连接串写法示例 + PLUGIN_ENABLE_FOLDERS = ["helloworld"] + + # 配置多个数据库连接的连接串写法示例 # HOSTNAME: 指数据库的IP地址、USERNAME:指数据库登录的用户名、PASSWORD:指数据库登录密码、PORT:指数据库开放的端口、DATABASE:指需要连接的数据库名称 # MSSQL: f"mssql+pymssql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=cp936" # MySQL: f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8" @@ -109,8 +102,7 @@ class BaseConfig: # 'testMsSQL': 'mssql+pymssql://test:123456@192.168.1.1:1433/test?charset=cp936', # 'testOracle': 'oracle+cx_oracle://test:123456@192.168.1.1:1521/test', # 'testSQLite': 'sqlite:///database.db - #} - + # } class TestingConfig(BaseConfig): diff --git a/applications/extensions/__init__.py b/applications/extensions/__init__.py index c698f5c..8220616 100644 --- a/applications/extensions/__init__.py +++ b/applications/extensions/__init__.py @@ -7,7 +7,7 @@ from .init_error_views import init_error_views from .init_mail import init_mail, mail as flask_mail from .init_apscheduler import init_scheduler from .init_upload import init_upload -from .init_dotenv import init_dotenv +from .init_migrate import init_migrate def init_plugs(app: Flask) -> None: @@ -18,4 +18,4 @@ def init_plugs(app: Flask) -> None: init_mail(app) init_scheduler(app) init_upload(app) - init_dotenv() + init_migrate(app) \ No newline at end of file diff --git a/applications/extensions/init_dotenv.py b/applications/extensions/init_dotenv.py deleted file mode 100644 index f67d3a4..0000000 --- a/applications/extensions/init_dotenv.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -from dotenv import load_dotenv - -root_path = os.path.abspath(os.path.dirname(__file__)).split('applications')[0] -# dot_env_path = os.path.join(root_path, '.env') -flask_env_path = os.path.join(root_path, '.flaskenv') - - -# if os.path.exists(dot_env_path): -# load_dotenv(dot_env_path) - -def init_dotenv(): - if os.path.exists(flask_env_path): - load_dotenv(flask_env_path) diff --git a/applications/extensions/init_migrate.py b/applications/extensions/init_migrate.py new file mode 100644 index 0000000..b178540 --- /dev/null +++ b/applications/extensions/init_migrate.py @@ -0,0 +1,11 @@ +from flask import Flask + +from applications.extensions.init_sqlalchemy import db +from flask_migrate import Migrate +from applications.models import * + +migrate = Migrate() + + +def init_migrate(app: Flask): + migrate.init_app(app, db) diff --git a/applications/extensions/init_template_directives.py b/applications/extensions/init_template_directives.py index bdfee67..86eda39 100644 --- a/applications/extensions/init_template_directives.py +++ b/applications/extensions/init_template_directives.py @@ -1,7 +1,11 @@ -from flask import session +from flask import session, current_app +from flask_login import current_user def init_template_directives(app): @app.template_global() def authorize(power): - return bool(power in session.get('permissions')) + if current_user.username != current_app.config.get("SUPERADMIN"): + return bool(power in session.get('permissions')) + else: + return True diff --git a/applications/view/__init__.py b/applications/view/__init__.py index 6b5cf78..ef00903 100644 --- a/applications/view/__init__.py +++ b/applications/view/__init__.py @@ -11,4 +11,4 @@ def init_view(app): register_rights_view(app) register_passport_views(app) register_dept_views(app) - register_plugin_views(app) + # register_plugin_views(app) diff --git a/applications/view/passport/__init__.py b/applications/view/passport/__init__.py index 371ef1f..e5a7c97 100644 --- a/applications/view/passport/__init__.py +++ b/applications/view/passport/__init__.py @@ -71,11 +71,11 @@ def login_post(): continue user_power.append(p.code) session['permissions'] = user_power - # 角色存入session - roles = [] - for role in current_user.role.all(): - roles.append(role.id) - session['role'] = [roles] + # # 角色存入session + # roles = [] + # for role in current_user.role.all(): + # roles.append(role.id) + # session['role'] = [roles] return success_api(msg="登录成功") login_log(request, uid=user.id, is_access=False) diff --git a/applications/view/rights/__init__.py b/applications/view/rights/__init__.py index 0293dbd..b113b9a 100644 --- a/applications/view/rights/__init__.py +++ b/applications/view/rights/__init__.py @@ -1,8 +1,6 @@ -from flask import Blueprint, Flask +from flask import Flask -rights_bp = Blueprint('rights', __name__, url_prefix='/rights') - -from . import routes +from .routes import rights_bp def register_rights_view(app: Flask): diff --git a/applications/view/rights/routes.py b/applications/view/rights/routes.py index 937eaaa..b335c1e 100644 --- a/applications/view/rights/routes.py +++ b/applications/view/rights/routes.py @@ -1,20 +1,147 @@ # 渲染配置 -from flask import jsonify -from flask_login import login_required +import copy +from collections import OrderedDict -from . import rights_bp -from ...common import admin +from flask import jsonify, current_app, Blueprint +from flask_login import login_required, current_user +from ...models import Power +from ...schemas import PowerOutSchema +rights_bp = Blueprint('rights', __name__, url_prefix='/rights') @rights_bp.get('/configs') @login_required def configs(): - return admin.get_render_config() + # 网站配置 + config = dict(logo={ + # 网站名称 + "title": current_app.config.get("SYSTEM_NAME"), + # 网站图标 + "image": "/static/admin/admin/images/logo.png" + # 菜单配置 + }, menu={ + # 菜单数据来源 + "data": "/rights/menu", + "collaspe": False, + # 是否同时只打开一个菜单目录 + "accordion": True, + "method": "GET", + # 是否开启多系统菜单模式 + "control": False, + # 顶部菜单宽度 PX + "controlWidth": 500, + # 默认选中的菜单项 + "select": "0", + # 是否开启异步菜单,false 时 data 属性设置为菜单数据,false 时为 json 文件或后端接口 + "async": True + }, tab={ + # 是否开启多选项卡 + "enable": True, + # 切换选项卡时,是否刷新页面状态 + "keepState": True, + # 是否开启 Tab 记忆 + "session": True, + # 最大可打开的选项卡数量 + "max": 30, + "index": { + # 标识 ID , 建议与菜单项中的 ID 一致 + "id": "10", + # 页面地址 + "href": "/admin/welcome", + # 标题 + "title": "首页" + } + }, theme={ + # 默认主题色,对应 colors 配置中的 ID 标识 + "defaultColor": "2", + # 默认的菜单主题 dark-theme 黑 / light-theme 白 + "defaultMenu": "dark-theme", + # 是否允许用户切换主题,false 时关闭自定义主题面板 + "allowCustom": True + }, colors=[{ + "id": "1", + "color": "#2d8cf0" + }, + { + "id": "2", + "color": "#5FB878" + }, + { + "id": "3", + "color": "#1E9FFF" + }, { + "id": "4", + "color": "#FFB800" + }, { + "id": "5", + "color": "darkgray" + } + ], links=current_app.config.get("SYSTEM_PANEL_LINKS"), other={ + # 主页动画时长 + "keepLoad": 1200, + # 布局顶部主题 + "autoHead": False + }, header=False) + return jsonify(config) # 菜单 @rights_bp.get('/menu') @login_required def menu(): - menu_tree = admin.make_menu_tree() - return jsonify(menu_tree) + + if current_user.username != current_app.config.get("SUPERADMIN"): + role = current_user.role + powers = [] + for i in role: + # 如果角色没有被启用就直接跳过 + if i.enable == 0: + continue + # 变量角色用户的权限 + for p in i.power: + # 如果权限关闭了就直接跳过 + if p.enable == 0: + continue + # 一二级菜单 + if int(p.type) in [0, 1] and p not in powers: + powers.append(p) + + power_schema = PowerOutSchema(many=True) # 用已继承 ma.ModelSchema 类的自定制类生成序列化类 + power_dict = power_schema.dump(powers) # 生成可序列化对象 + power_dict.sort(key=lambda x: (x['parent_id'], x['id']), reverse=True) + + menu_dict = OrderedDict() + for _dict in power_dict: + if _dict['id'] in menu_dict: + # 当前节点添加子节点 + _dict['children'] = copy.deepcopy(menu_dict[_dict['id']]) + _dict['children'].sort(key=lambda item: item['sort']) + # 删除子节点 + del menu_dict[_dict['id']] + + if _dict['parent_id'] not in menu_dict: + + menu_dict[_dict['parent_id']] = [_dict] + else: + menu_dict[_dict['parent_id']].append(_dict) + return jsonify(sorted(menu_dict.get(0), key=lambda item: item['sort'])) + else: + powers = Power.query.all() + power_schema = PowerOutSchema(many=True) # 用已继承 ma.ModelSchema 类的自定制类生成序列化类 + power_dict = power_schema.dump(powers) # 生成可序列化对象 + power_dict.sort(key=lambda x: (x['parent_id'], x['id']), reverse=True) + + menu_dict = OrderedDict() + for _dict in power_dict: + if _dict['id'] in menu_dict: + # 当前节点添加子节点 + _dict['children'] = copy.deepcopy(menu_dict[_dict['id']]) + _dict['children'].sort(key=lambda item: item['sort']) + # 删除子节点 + del menu_dict[_dict['id']] + + if _dict['parent_id'] not in menu_dict: + menu_dict[_dict['parent_id']] = [_dict] + else: + menu_dict[_dict['parent_id']].append(_dict) + return jsonify(sorted(menu_dict.get(0), key=lambda item: item['sort'])) diff --git a/migrations/README b/migrations/README index 98e4f9c..0e04844 100644 --- a/migrations/README +++ b/migrations/README @@ -1 +1 @@ -Generic single-database configuration. \ No newline at end of file +Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini index f8ed480..ec9d45c 100644 --- a/migrations/alembic.ini +++ b/migrations/alembic.ini @@ -11,7 +11,7 @@ # Logging configuration [loggers] -keys = root,sqlalchemy,alembic +keys = root,sqlalchemy,alembic,flask_migrate [handlers] keys = console @@ -34,6 +34,11 @@ level = INFO handlers = qualname = alembic +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + [handler_console] class = StreamHandler args = (sys.stderr,) diff --git a/migrations/env.py b/migrations/env.py index fb5dbb3..89f80b2 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -1,10 +1,6 @@ -from __future__ import with_statement - import logging from logging.config import fileConfig -from sqlalchemy import engine_from_config -from sqlalchemy import pool from flask import current_app from alembic import context @@ -13,26 +9,48 @@ from alembic import context # access to the values within the .ini file in use. config = context.config -# Interpret the configs file for Python logging. +# Interpret the config file for Python logging. # This line sets up loggers basically. fileConfig(config.config_file_name) logger = logging.getLogger('alembic.env') + +def get_engine(): + try: + # this works with Flask-SQLAlchemy<3 and Alchemical + return current_app.extensions['migrate'].db.get_engine() + except TypeError: + # this works with Flask-SQLAlchemy>=3 + return current_app.extensions['migrate'].db.engine + + +def get_engine_url(): + try: + return get_engine().url.render_as_string(hide_password=False).replace( + '%', '%%') + except AttributeError: + return str(get_engine().url).replace('%', '%%') + + # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata -config.set_main_option( - 'sqlalchemy.url', - str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) -target_metadata = current_app.extensions['migrate'].db.metadata +config.set_main_option('sqlalchemy.url', get_engine_url()) +target_db = current_app.extensions['migrate'].db -# other values from the configs, defined by the needs of env.py, +# other values from the config, defined by the needs of env.py, # can be acquired: -# my_important_option = configs.get_main_option("my_important_option") +# my_important_option = config.get_main_option("my_important_option") # ... etc. +def get_metadata(): + if hasattr(target_db, 'metadatas'): + return target_db.metadatas[None] + return target_db.metadata + + def run_migrations_offline(): """Run migrations in 'offline' mode. @@ -47,7 +65,7 @@ def run_migrations_offline(): """ url = config.get_main_option("sqlalchemy.url") context.configure( - url=url, target_metadata=target_metadata, literal_binds=True + url=url, target_metadata=get_metadata(), literal_binds=True ) with context.begin_transaction(): @@ -72,16 +90,12 @@ def run_migrations_online(): directives[:] = [] logger.info('No changes in schema detected.') - connectable = engine_from_config( - config.get_section(config.config_ini_section), - prefix='sqlalchemy.', - poolclass=pool.NullPool, - ) + connectable = get_engine() with connectable.connect() as connection: context.configure( connection=connection, - target_metadata=target_metadata, + target_metadata=get_metadata(), process_revision_directives=process_revision_directives, **current_app.extensions['migrate'].configure_args ) diff --git a/migrations/versions/7634e028e338_.py b/migrations/versions/7634e028e338_.py deleted file mode 100644 index 53491e4..0000000 --- a/migrations/versions/7634e028e338_.py +++ /dev/null @@ -1,28 +0,0 @@ -"""empty message - -Revision ID: 7634e028e338 -Revises: 8b664608a7c7 -Create Date: 2021-06-01 16:43:50.853692 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '7634e028e338' -down_revision = '8b664608a7c7' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('admin_user', sa.Column('dept_id', sa.Integer(), nullable=True, comment='部门id')) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('admin_user', 'dept_id') - # ### end Alembic commands ### diff --git a/migrations/versions/8b664608a7c7_.py b/migrations/versions/8b664608a7c7_.py deleted file mode 100644 index 1917956..0000000 --- a/migrations/versions/8b664608a7c7_.py +++ /dev/null @@ -1,42 +0,0 @@ -"""empty message - -Revision ID: 8b664608a7c7 -Revises: ec21e19825ff -Create Date: 2021-06-01 14:37:20.327189 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '8b664608a7c7' -down_revision = 'ec21e19825ff' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('admin_dept', - sa.Column('id', sa.Integer(), nullable=False, comment='部门ID'), - sa.Column('parent_id', sa.Integer(), nullable=True, comment='父级编号'), - sa.Column('dept_name', sa.String(length=50), nullable=True, comment='部门名称'), - sa.Column('sort', sa.Integer(), nullable=True, comment='排序'), - sa.Column('leader', sa.String(length=50), nullable=True, comment='负责人'), - sa.Column('phone', sa.String(length=20), nullable=True, comment='联系方式'), - sa.Column('email', sa.String(length=50), nullable=True, comment='邮箱'), - sa.Column('status', sa.Integer(), nullable=True, comment='状态(1开启,0关闭)'), - sa.Column('remark', sa.Text(), nullable=True, comment='备注'), - sa.Column('address', sa.String(length=255), 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_dept') - # ### end Alembic commands ### diff --git a/migrations/versions/ec21e19825ff_.py b/migrations/versions/b7a6be37d1f3_.py similarity index 72% rename from migrations/versions/ec21e19825ff_.py rename to migrations/versions/b7a6be37d1f3_.py index bf88f1d..f0b261c 100644 --- a/migrations/versions/ec21e19825ff_.py +++ b/migrations/versions/b7a6be37d1f3_.py @@ -1,16 +1,16 @@ """empty message -Revision ID: ec21e19825ff +Revision ID: b7a6be37d1f3 Revises: -Create Date: 2021-04-27 11:54:00.453274 +Create Date: 2023-04-06 00:18:14.996470 """ from alembic import op import sqlalchemy as sa - +from sqlalchemy.dialects import mysql # revision identifiers, used by Alembic. -revision = 'ec21e19825ff' +revision = 'b7a6be37d1f3' down_revision = None branch_labels = None depends_on = None @@ -30,6 +30,21 @@ def upgrade(): sa.Column('create_time', sa.DateTime(), nullable=True), sa.PrimaryKeyConstraint('id') ) + op.create_table('admin_dept', + sa.Column('id', sa.Integer(), nullable=False, comment='部门ID'), + sa.Column('parent_id', sa.Integer(), nullable=True, comment='父级编号'), + sa.Column('dept_name', sa.String(length=50), nullable=True, comment='部门名称'), + sa.Column('sort', sa.Integer(), nullable=True, comment='排序'), + sa.Column('leader', sa.String(length=50), nullable=True, comment='负责人'), + sa.Column('phone', sa.String(length=20), nullable=True, comment='联系方式'), + sa.Column('email', sa.String(length=50), nullable=True, comment='邮箱'), + sa.Column('status', sa.Integer(), nullable=True, comment='状态(1开启,0关闭)'), + sa.Column('remark', sa.Text(), nullable=True, comment='备注'), + sa.Column('address', sa.String(length=255), 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_dict_data', sa.Column('id', sa.Integer(), nullable=False), sa.Column('data_label', sa.String(length=255), nullable=True, comment='字典类型名称'), @@ -52,6 +67,15 @@ def upgrade(): sa.Column('update_time', sa.DateTime(), nullable=True, comment='更新时间'), sa.PrimaryKeyConstraint('id') ) + op.create_table('admin_mail', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='邮件编号'), + sa.Column('receiver', sa.String(length=1024), nullable=True, comment='收件人邮箱'), + sa.Column('subject', sa.String(length=128), nullable=True, comment='邮件主题'), + sa.Column('content', sa.Text(), nullable=True, comment='邮件正文'), + sa.Column('user_id', sa.Integer(), nullable=True, comment='发送人id'), + sa.Column('create_at', sa.DateTime(), nullable=True, comment='创建时间'), + sa.PrimaryKeyConstraint('id') + ) op.create_table('admin_photo', sa.Column('id', sa.Integer(), nullable=False), sa.Column('name', sa.String(length=255), nullable=False), @@ -96,6 +120,7 @@ def upgrade(): sa.Column('remark', sa.String(length=255), nullable=True, comment='备注'), sa.Column('password_hash', sa.String(length=128), nullable=True, comment='哈希密码'), sa.Column('enable', sa.Integer(), nullable=True, comment='启用'), + sa.Column('dept_id', sa.Integer(), nullable=True, comment='部门id'), sa.Column('create_at', sa.DateTime(), nullable=True, comment='创建时间'), sa.Column('update_at', sa.DateTime(), nullable=True, comment='创建时间'), sa.PrimaryKeyConstraint('id') @@ -116,18 +141,35 @@ def upgrade(): sa.ForeignKeyConstraint(['user_id'], ['admin_user.id'], ), sa.PrimaryKeyConstraint('id') ) + with op.batch_alter_table('apscheduler_jobs', schema=None) as batch_op: + batch_op.drop_index('ix_apscheduler_jobs_next_run_time') + + op.drop_table('apscheduler_jobs') # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### + op.create_table('apscheduler_jobs', + sa.Column('id', mysql.VARCHAR(length=191), nullable=False), + sa.Column('next_run_time', mysql.DOUBLE(asdecimal=True), nullable=True), + sa.Column('job_state', sa.BLOB(), nullable=False), + sa.PrimaryKeyConstraint('id'), + mysql_default_charset='utf8mb3', + mysql_engine='InnoDB' + ) + with op.batch_alter_table('apscheduler_jobs', schema=None) as batch_op: + batch_op.create_index('ix_apscheduler_jobs_next_run_time', ['next_run_time'], unique=False) + op.drop_table('admin_user_role') op.drop_table('admin_role_power') op.drop_table('admin_user') op.drop_table('admin_role') op.drop_table('admin_power') op.drop_table('admin_photo') + op.drop_table('admin_mail') op.drop_table('admin_dict_type') op.drop_table('admin_dict_data') + op.drop_table('admin_dept') op.drop_table('admin_admin_log') # ### end Alembic commands ### diff --git a/pear.sql b/pear.sql index e33d78a..771e422 100644 --- a/pear.sql +++ b/pear.sql @@ -1,158 +1,4 @@ -/* - Navicat Premium Data Transfer - Source Server : phpmystudy - Source Server Type : MySQL - Source Server Version : 50726 - Source Host : localhost:3306 - Source Schema : pearadminflask - - Target Server Type : MySQL - Target Server Version : 50726 - File Encoding : 65001 - - Date: 07/07/2021 13:52:10 -*/ - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ---------------------------- --- Table structure for admin_admin_log --- ---------------------------- -DROP TABLE IF EXISTS `admin_admin_log`; -CREATE TABLE `admin_admin_log` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `method` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, - `uid` int(11) NULL DEFAULT NULL, - `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, - `desc` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL, - `ip` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, - `user_agent` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL, - `create_time` datetime(0) NULL DEFAULT NULL, - `success` int(11) NULL DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1485 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; - --- ---------------------------- --- Records of admin_admin_log --- ---------------------------- - --- ---------------------------- --- Table structure for admin_dept --- ---------------------------- -DROP TABLE IF EXISTS `admin_dept`; -CREATE TABLE `admin_dept` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '部门ID', - `parent_id` int(11) NULL DEFAULT NULL COMMENT '父级编号', - `dept_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '部门名称', - `sort` int(11) NULL DEFAULT NULL COMMENT '排序', - `leader` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '负责人', - `phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '联系方式', - `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '邮箱', - `status` int(11) NULL DEFAULT NULL COMMENT '状态(1开启,0关闭)', - `remark` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '备注', - `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '详细地址', - `create_at` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - `update_at` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; - --- ---------------------------- --- Records of admin_dept --- ---------------------------- -INSERT INTO `admin_dept` VALUES (1, 0, '总公司', 1, '就眠仪式', '12312345679', '123qq.com', 1, NULL, '这是总公司', NULL, '2021-06-01 17:23:20'); -INSERT INTO `admin_dept` VALUES (4, 1, '济南分公司', 2, '就眠仪式', '12312345678', '1234qq.com', 1, NULL, '这是济南', '2021-06-01 17:24:33', '2021-06-01 17:25:19'); -INSERT INTO `admin_dept` VALUES (5, 1, '唐山分公司', 4, 'mkg', '12312345678', '123@qq.com', 1, NULL, '这是唐山', '2021-06-01 17:25:15', '2021-06-01 17:25:20'); -INSERT INTO `admin_dept` VALUES (7, 4, '济南分公司开发部', 5, '就眠仪式', '12312345678', '123@qq.com', 1, NULL, '测试', '2021-06-01 17:27:39', '2021-06-01 17:27:39'); -INSERT INTO `admin_dept` VALUES (8, 5, '唐山测试部', 6, 'mkg', '12312345678', '123@qq.com', 1, NULL, '测试部', '2021-06-01 17:28:27', '2021-06-01 17:28:27'); - --- ---------------------------- --- Table structure for admin_dict_data --- ---------------------------- -DROP TABLE IF EXISTS `admin_dict_data`; -CREATE TABLE `admin_dict_data` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `data_label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '字典类型名称', - `data_value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '字典类型标识', - `type_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '字典类型描述', - `is_default` int(11) NULL DEFAULT NULL COMMENT '是否默认', - `enable` int(11) NULL DEFAULT NULL COMMENT '是否开启', - `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注', - `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; - --- ---------------------------- --- Records of admin_dict_data --- ---------------------------- -INSERT INTO `admin_dict_data` VALUES (8, '男', 'boy', 'user_sex', NULL, 1, '男 : body', '2021-04-16 13:36:34', '2021-04-16 14:05:06'); -INSERT INTO `admin_dict_data` VALUES (9, '女', 'girl', 'user_sex', NULL, 1, '女 : girl', '2021-04-16 13:36:55', '2021-04-16 13:36:55'); - --- ---------------------------- --- Table structure for admin_dict_type --- ---------------------------- -DROP TABLE IF EXISTS `admin_dict_type`; -CREATE TABLE `admin_dict_type` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `type_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '字典类型名称', - `type_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '字典类型标识', - `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '字典类型描述', - `enable` int(11) NULL DEFAULT NULL COMMENT '是否开启', - `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; - --- ---------------------------- --- Records of admin_dict_type --- ---------------------------- -INSERT INTO `admin_dict_type` VALUES (1, '用户性别', 'user_sex', '用户性别', 1, NULL, '2021-04-16 13:37:11'); - --- ---------------------------- --- Table structure for admin_mail --- ---------------------------- -DROP TABLE IF EXISTS `admin_mail`; -CREATE TABLE `admin_mail` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '邮件编号', - `receiver` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '收件人邮箱', - `subject` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮件主题', - `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '邮件正文', - `user_id` int(11) NULL DEFAULT NULL COMMENT '发送人id', - `create_at` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; - --- ---------------------------- --- Records of admin_mail --- ---------------------------- -INSERT INTO `admin_mail` VALUES (1, '1242733702@qq.com', 'pear-admin-flask', 'pear-admin-flask', 1, '2022-10-11 13:41:21'); -INSERT INTO `admin_mail` VALUES (4, '1242733702@qq.com', '湖人总冠军', '湖人总冠军', 1, '2022-10-11 14:03:30'); -INSERT INTO `admin_mail` VALUES (5, '1242733702@qq.com', '这是flask测试邮箱', '正文', 1, '2022-10-11 14:10:30'); - - --- ---------------------------- --- Table structure for admin_photo --- ---------------------------- -DROP TABLE IF EXISTS `admin_photo`; -CREATE TABLE `admin_photo` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, - `href` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, - `mime` char(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, - `size` char(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, - `create_time` datetime(0) NULL DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; - --- ---------------------------- --- Records of admin_photo --- ---------------------------- -INSERT INTO `admin_photo` VALUES (3, '6958819_pear-admin_1607443454_1.png', 'http://127.0.0.1:5000/_uploads/photos/6958819_pear-admin_1607443454_1.png', 'image/png', '2204', '2021-03-19 18:53:02'); -INSERT INTO `admin_photo` VALUES (17, '1617291580000.jpg', 'http://127.0.0.1:5000/_uploads/photos/1617291580000.jpg', 'image/png', '94211', '2021-04-01 23:39:41'); - --- ---------------------------- -- Table structure for admin_power -- ---------------------------- DROP TABLE IF EXISTS `admin_power`; @@ -216,30 +62,7 @@ INSERT INTO `admin_power` VALUES (61, '插件管理', '1', 'admin:plugin:main', INSERT INTO `admin_power` VALUES (62, '启禁插件', '2', 'admin:plugin:enable', '', '', '61', 'layui-icon ', 1, '2022-12-18 13:25:37', '2022-12-18 13:25:37', 1); INSERT INTO `admin_power` VALUES (63, '删除插件', '2', 'admin:plugin:remove', '', '', '61', 'layui-icon layui-icon ', 2, '2022-12-18 13:26:30', '2022-12-18 13:27:17', 1); --- ---------------------------- --- Table structure for admin_role --- ---------------------------- -DROP TABLE IF EXISTS `admin_role`; -CREATE TABLE `admin_role` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色ID', - `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '角色名称', - `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '角色标识', - `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注', - `details` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '详情', - `sort` int(11) NULL DEFAULT NULL COMMENT '排序', - `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', - `enable` int(11) NULL DEFAULT NULL COMMENT '是否启用', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; --- ---------------------------- --- Records of admin_role --- ---------------------------- -INSERT INTO `admin_role` VALUES (1, '管理员', 'admin', NULL, '管理员', 1, NULL, NULL, 1); -INSERT INTO `admin_role` VALUES (2, '普通用户', 'common', NULL, '只有查看,没有增删改权限', 2, '2021-03-22 20:02:38', '2021-04-01 22:29:56', 1); - --- ---------------------------- -- Table structure for admin_role_power -- ---------------------------- DROP TABLE IF EXISTS `admin_role_power`; @@ -257,81 +80,8 @@ CREATE TABLE `admin_role_power` ( -- ---------------------------- -- Records of admin_role_power -- ---------------------------- -INSERT INTO `admin_role_power` VALUES (265, 1, 2); -INSERT INTO `admin_role_power` VALUES (266, 3, 2); -INSERT INTO `admin_role_power` VALUES (267, 4, 2); -INSERT INTO `admin_role_power` VALUES (268, 9, 2); -INSERT INTO `admin_role_power` VALUES (269, 12, 2); -INSERT INTO `admin_role_power` VALUES (270, 13, 2); -INSERT INTO `admin_role_power` VALUES (271, 17, 2); -INSERT INTO `admin_role_power` VALUES (272, 18, 2); -INSERT INTO `admin_role_power` VALUES (273, 44, 2); -INSERT INTO `admin_role_power` VALUES (274, 48, 2); -INSERT INTO `admin_role_power` VALUES (334, 1, 1); -INSERT INTO `admin_role_power` VALUES (335, 3, 1); -INSERT INTO `admin_role_power` VALUES (336, 4, 1); -INSERT INTO `admin_role_power` VALUES (337, 9, 1); -INSERT INTO `admin_role_power` VALUES (338, 12, 1); -INSERT INTO `admin_role_power` VALUES (339, 13, 1); -INSERT INTO `admin_role_power` VALUES (340, 17, 1); -INSERT INTO `admin_role_power` VALUES (341, 18, 1); -INSERT INTO `admin_role_power` VALUES (342, 21, 1); -INSERT INTO `admin_role_power` VALUES (343, 22, 1); -INSERT INTO `admin_role_power` VALUES (344, 23, 1); -INSERT INTO `admin_role_power` VALUES (345, 24, 1); -INSERT INTO `admin_role_power` VALUES (346, 25, 1); -INSERT INTO `admin_role_power` VALUES (347, 26, 1); -INSERT INTO `admin_role_power` VALUES (348, 27, 1); -INSERT INTO `admin_role_power` VALUES (349, 28, 1); -INSERT INTO `admin_role_power` VALUES (350, 29, 1); -INSERT INTO `admin_role_power` VALUES (351, 30, 1); -INSERT INTO `admin_role_power` VALUES (352, 31, 1); -INSERT INTO `admin_role_power` VALUES (353, 32, 1); -INSERT INTO `admin_role_power` VALUES (354, 44, 1); -INSERT INTO `admin_role_power` VALUES (355, 45, 1); -INSERT INTO `admin_role_power` VALUES (356, 46, 1); -INSERT INTO `admin_role_power` VALUES (357, 47, 1); -INSERT INTO `admin_role_power` VALUES (358, 48, 1); -INSERT INTO `admin_role_power` VALUES (359, 49, 1); -INSERT INTO `admin_role_power` VALUES (360, 50, 1); -INSERT INTO `admin_role_power` VALUES (361, 51, 1); -INSERT INTO `admin_role_power` VALUES (362, 52, 1); -INSERT INTO `admin_role_power` VALUES (363, 53, 1); -INSERT INTO `admin_role_power` VALUES (364, 54, 1); -INSERT INTO `admin_role_power` VALUES (365, 55, 1); -INSERT INTO `admin_role_power` VALUES (366, 56, 1); -INSERT INTO `admin_role_power` VALUES (367, 57, 1); -INSERT INTO `admin_role_power` VALUES (368, 58, 1); -INSERT INTO `admin_role_power` VALUES (369, 59, 1); -INSERT INTO `admin_role_power` VALUES (370, 60, 1); -INSERT INTO `admin_role_power` VALUES (371, 61, 1); -INSERT INTO `admin_role_power` VALUES (372, 62, 1); -INSERT INTO `admin_role_power` VALUES (373, 63, 1); +1,3,4,9,12,13,17,18,44,48 --- ---------------------------- --- Table structure for admin_user --- ---------------------------- -DROP TABLE IF EXISTS `admin_user`; -CREATE TABLE `admin_user` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID', - `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '用户名', - `password_hash` varchar(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '哈希密码', - `create_at` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - `update_at` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', - `enable` int(11) NULL DEFAULT NULL COMMENT '启用', - `realname` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '真实名字', - `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注', - `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '头像', - `dept_id` int(11) NULL DEFAULT NULL COMMENT '部门id', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; - --- ---------------------------- --- Records of admin_user --- ---------------------------- -INSERT INTO `admin_user` VALUES (1, 'admin', 'pbkdf2:sha256:150000$raM7mDSr$58fe069c3eac01531fc8af85e6fc200655dd2588090530084d182e6ec9d52c85', NULL, '2021-06-01 17:28:55', 1, '超级管理', '要是不能把握时机,就要终身蹭蹬,一事无成!', 'http://127.0.0.1:5000/_uploads/photos/1617291580000.jpg', 1); -INSERT INTO `admin_user` VALUES (7, 'test', 'pbkdf2:sha256:150000$cRS8bYNh$adb57e64d929863cf159f924f74d0634f1fecc46dba749f1bfaca03da6d2e3ac', '2021-03-22 20:03:42', '2021-06-01 17:29:47', 1, '超级管理', '要是不能把握时机,就要终身蹭蹬,一事无成', '/static/admin/admin/images/avatar.jpg', 1); -INSERT INTO `admin_user` VALUES (8, 'wind', 'pbkdf2:sha256:150000$skME1obT$6a2c20cd29f89d7d2f21d9e373a7e3445f70ebce3ef1c3a555e42a7d17170b37', '2021-06-01 17:30:39', '2021-06-01 17:30:52', 1, '风', NULL, '/static/admin/admin/images/avatar.jpg', 7); -- ---------------------------- -- Table structure for admin_user_role @@ -354,19 +104,3 @@ CREATE TABLE `admin_user_role` ( INSERT INTO `admin_user_role` VALUES (21, 1, 1); INSERT INTO `admin_user_role` VALUES (22, 7, 2); INSERT INTO `admin_user_role` VALUES (24, 8, 2); - --- ---------------------------- --- Table structure for alembic_version --- ---------------------------- -DROP TABLE IF EXISTS `alembic_version`; -CREATE TABLE `alembic_version` ( - `version_num` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, - PRIMARY KEY (`version_num`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC; - --- ---------------------------- --- Records of alembic_version --- ---------------------------- -INSERT INTO `alembic_version` VALUES ('7634e028e338'); - -SET FOREIGN_KEY_CHECKS = 1; diff --git a/requirement.txt b/requirement.txt index 05ae4e1..9924434 100644 --- a/requirement.txt +++ b/requirement.txt @@ -12,4 +12,5 @@ Flask-Mail sqlparse Pillow python-dotenv -validators \ No newline at end of file +validators +cryptography \ No newline at end of file