├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── collection ├── __init__.py └── v1 │ ├── __init__.py │ ├── admin.py │ ├── base.py │ ├── document.py │ ├── folder.py │ ├── interface.py │ ├── log.py │ ├── menu.py │ └── role.py ├── conf ├── __init__.py └── setting.py ├── libs ├── __init__.py ├── aliyun.py ├── captcha.py ├── code.py ├── scope.py └── utils.py ├── logs └── __init__.py ├── models ├── __init__.py ├── log.py └── system.py ├── requirements.txt ├── routes ├── __init__.py ├── token_auth.py └── v1 │ ├── __init__.py │ ├── admin.py │ ├── base.py │ ├── document.py │ ├── folder.py │ ├── interface.py │ ├── log.py │ ├── menu.py │ └── role.py ├── services └── __init__.py ├── sockets └── __init__.py ├── start.py ├── static ├── favicon.ico └── image │ └── markdown │ ├── admin.jpg │ ├── desktop.png │ ├── interface.jpg │ ├── interface_edit.jpg │ ├── log.jpg │ ├── markdown.jpg │ ├── menu.jpg │ ├── role_edit.jpg │ └── upload.jpg ├── test └── test.py ├── tools └── manage.py └── validate ├── __init__.py └── v1 ├── __init__.py ├── admin.py ├── base.py ├── document.py ├── folder.py ├── interface.py ├── log.py ├── menu.py └── role.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | sql 3 | logs/*.log 4 | logs/*.log.* 5 | .vscode/*.json 6 | document 7 | sessions 8 | /tools/migrations 9 | /tools/*.mmdb 10 | /conf/aliyun.py 11 | excel 12 | caches -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 huzidabanzhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###
如果对您有帮助,请帮忙点右上角 "Star" 支持一下 谢谢! 2 | 3 | ## 预览 4 | 5 | [Preview](https://test.ig132n.cn/ "Preview") 6 | 7 | 测试账号:Test 密码:111111 8 | 9 | VUE 仓库地址 [章胖胖](https://github.com/huzidabanzhang/python-admin-pm "章胖胖") 10 | 11 | ## 功能 12 | 13 | - 管理员管理 14 | - 菜单管理 15 | - 角色管理 16 | - 接口管理 17 | - 文档管理 18 | - 数据库管理 19 | - 日志查看 20 | 21 | ## 注意 22 | 23 | tools/manage.py 为数据库版本控制的 py,具体的使用介绍你可以看: [章胖胖的笔记](https://huzidabanzhang.github.io/notes/2020-03-30.html#python-flask-migrate-%E8%BF%81%E7%A7%BB%E6%95%B0%E6%8D%AE%E5%BA%93 "章胖胖的笔记") 24 | 25 | 其中 conf 下有 aliyun.py 主要放密钥 需要的自己新建 26 | 27 | 加入 GeoLite2 的 IP 转换地址 28 | 29 | ## 启动 30 | 31 | 第一次启动前,先创建一个数据库,然后在目录 conf/setting.py 里面类中修改连接数据库信息 32 | 33 | ```shell 34 | python start.py # 启动服务 35 | ``` 36 | 37 | 前端页面打开第一次会提示是否初始化数据库,初始化后会提示弹出 Admin 的初始密码登录即可. 38 | 39 | ## 验证器 40 | 41 | [验证器](https://github.com/huzidabanzhang/python-admin/blob/master/trunk/validate/__init__.py "验证器") 42 | 43 | ```python 44 | # 路由中引用验证器 45 | from validate import validate_form 46 | from validate.v1.admin import params 47 | validate = validate_form(params) 48 | 49 | @route_admin.route('/Login', methods=['POST'], endpoint='Login') # endpoint这个一定要加不然报错 50 | @validate.form('Login') # 需要验证的场景 51 | ``` 52 | 53 | ```python 54 | # 验证器路径/validate/v1 55 | params = { 56 | # 引用验证字段场景 57 | 'Test': ['admin_id[]', 'disable'], 58 | # 验证场景中需要修改字段里面的内容或增加字段内容 59 | 60 | # 用dict里面加入field这个很重要要判断是哪个字段dict会覆盖原来的判断条件 61 | 'Test2': [{ 62 | 'field': 'code', 63 | 'required': False 64 | }], 65 | # 验证字段 66 | 'fields': { 67 | 'code': { 68 | 'name': u'验证码', 69 | 'type': 'str', # 字段类型包括str, list, int, boolean, ic, phone, email, time 70 | 'min': 4, # 字符长度最小值 其中list判断长度 71 | 'max': 4, # 字符长度最大值 其中list判断长度 72 | 'between': [888, 999], # 字符必须在list中 73 | 'required': True, # 是否必填 74 | 'default': 111 # 默认值 75 | }, 76 | 'email': { 77 | 'name': u'邮件', 78 | 'type': 'email' 79 | }, 80 | 'sex': { 81 | 'name': u'性别', 82 | 'type': 'int', 83 | 'default': 1 84 | }, 85 | 'disable': { 86 | 'name': u'可见性', 87 | 'type': 'boolean', 88 | 'required': True 89 | }, 90 | 'admin_id[]': { 91 | 'name': u'管理员编号', 92 | 'type': 'list', 93 | 'required': True 94 | } 95 | } 96 | } 97 | ``` 98 | 99 | ## 计划 100 | 101 | 已经到 python3 102 | 103 | python2 的移动到了分支中 104 | 105 | ![首页](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/desktop.png "首页") 106 | 107 | ![菜单](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/menu.jpg "菜单") 108 | 109 | ![鉴权](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/role.jpg "鉴权") 110 | 111 | ![鉴权编辑](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/role_edit.jpg "鉴权编辑") 112 | 113 | ![接口](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/interface.jpg "接口") 114 | 115 | ![接口编辑](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/interface_edit.jpg "接口编辑") 116 | 117 | ![管理员](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/admin.jpg "管理员") 118 | 119 | ![日志](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/log.jpg "日志") 120 | 121 | ![markdown](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/markdown.jpg "markdown") 122 | 123 | ![上传](https://github.com/huzidabanzhang/python-admin/blob/master/static/image/markdown/upload.jpg "上传") 124 | 125 | ........ 126 | 127 | 128 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-04 10:22:00 7 | @LastEditTime: 2019-09-04 10:22:00 8 | @LastEditors: Zpp 9 | ''' -------------------------------------------------------------------------------- /collection/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Author: Zpp 5 | @Date: 2019-09-09 10:01:57 6 | @LastEditors: Zpp 7 | @LastEditTime: 2020-03-30 14:53:35 8 | @Description: 9 | ''' 10 | 11 | -------------------------------------------------------------------------------- /collection/v1/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Description: 3 | @Author: Zpp 4 | @Date: 2020-03-30 14:54:05 5 | @LastEditors: Zpp 6 | @LastEditTime: 2020-03-30 14:54:05 7 | ''' 8 | -------------------------------------------------------------------------------- /collection/v1/admin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-09 10:02:39 7 | LastEditTime: 2020-11-26 10:09:46 8 | LastEditors: Zpp 9 | ''' 10 | from flask import request 11 | from models import db 12 | from models.system import Admin, Role, Menu, LoginLock, Interface 13 | from conf.setting import _config, base_info, default 14 | import uuid 15 | import datetime 16 | import copy 17 | import json 18 | 19 | 20 | class AdminModel(): 21 | def QueryAdminByParamRequest(self, params, page=1, page_size=20, order_by='id'): 22 | ''' 23 | 管理员列表 24 | ''' 25 | s = db.session() 26 | try: 27 | data = {} 28 | for i in ['role_id', 'disable']: 29 | if i in params: 30 | data[i] = params[i] 31 | 32 | result = Admin.query.filter_by(**data).order_by(order_by).paginate(page, page_size, error_out=False) 33 | 34 | return {'data': [value.to_json() for value in result.items], 'total': result.total} 35 | except Exception as e: 36 | print(e) 37 | return str(e) 38 | 39 | def CreateAdminRequest(self, params): 40 | ''' 41 | 新建管理员 42 | ''' 43 | s = db.session() 44 | try: 45 | role = s.query(Role).filter(Role.role_id == params['role_id']).first() 46 | 47 | if not role: 48 | return str('角色不存在') 49 | 50 | if role.mark == default['role_mark']: 51 | return str('不能设为超级管理员角色') 52 | 53 | admin = s.query(Admin).filter(Admin.username == params['username']).first() 54 | 55 | if admin: 56 | return str('管理员已存在') 57 | 58 | item = Admin( 59 | admin_id=str(uuid.uuid4()), 60 | username=params['username'], 61 | password=_config.get_md5(params['password']), 62 | sex=int(params['sex']), 63 | email=params['email'], 64 | nickname=params['nickname'], 65 | avatar=params['avatar'], 66 | role_id=params['role_id'], 67 | disable=params['disable'] 68 | ) 69 | s.add(item) 70 | role.admins.append(item) 71 | s.commit() 72 | return True 73 | except Exception as e: 74 | s.rollback() 75 | print(e) 76 | return str(e) 77 | 78 | def GetAdminRequest(self, username, password): 79 | ''' 80 | 登录管理员 81 | ''' 82 | s = db.session() 83 | try: 84 | admin = s.query(Admin).filter(Admin.username == username).first() 85 | if not admin: 86 | return str('管理员不存在') 87 | 88 | is_lock = s.query(LoginLock).filter(LoginLock.user_id == admin.admin_id).first() 89 | if is_lock and is_lock.number >= base_info['lock_times']: 90 | if datetime.datetime.now() < is_lock.lock_time: 91 | return str('账号锁定中, 请在%s分钟后重试' % (is_lock.lock_time - datetime.datetime.now()).minute) 92 | 93 | if admin.password != _config.get_md5(password): 94 | number = 1 95 | if is_lock: 96 | number = is_lock.number + 1 97 | if number >= base_info['lock_times']: 98 | add_minutes = (number - base_info['lock_times']) + 1 99 | is_lock.lock_time = datetime.datetime.now() + datetime.timedelta(minutes=add_minutes) 100 | is_lock.number = number 101 | else: 102 | s.add(LoginLock( 103 | lock_id=str(uuid.uuid4()), 104 | user_id=admin.admin_id, 105 | flag=False, 106 | number=number, 107 | ip=request.headers['X-Real-Ip'] if 'X-Real-Ip' in request.headers else request.remote_addr 108 | )) 109 | s.commit() 110 | if number - base_info['lock_times'] == 0: 111 | return str('密码不正确, 您的账号已经被锁定, 请在%s分钟后重试' % add_minutes) 112 | else: 113 | return str('密码不正确, 还有%s次机会' % (base_info['lock_times'] - number)) 114 | 115 | if admin.disable: 116 | return str('管理员被禁用') 117 | 118 | role = s.query(Role).filter(Role.role_id == admin.role_id).first() 119 | interface = [value.to_json() for value in role.interfaces] if role else [] 120 | menu = [value.to_json() for value in role.menus.order_by(Menu.sort, Menu.id)] if role else [] 121 | 122 | user = copy.deepcopy(admin.to_json()) 123 | user['mark'] = role.mark 124 | 125 | # 删除不必要信息 126 | del_list = ['id', 'create_time', 'update_time'] 127 | for i in del_list: 128 | del user[i] 129 | 130 | # 登录成功删除掉原来的锁定记录 131 | if is_lock: 132 | s.delete(is_lock) 133 | s.commit() 134 | 135 | return { 136 | 'menus': menu, 137 | 'interface': interface, 138 | 'user': user 139 | } 140 | except Exception as e: 141 | print(e) 142 | return str(e) 143 | 144 | def ModifyAdminRequest(self, admin_id, params): 145 | ''' 146 | 修改管理员信息 147 | ''' 148 | s = db.session() 149 | try: 150 | admin = s.query(Admin).filter(Admin.admin_id == admin_id).first() 151 | if not admin: 152 | return str('管理员不存在') 153 | 154 | role = s.query(Role).filter(Role.role_id == params['role_id']).first() 155 | 156 | if not role: 157 | return str('角色不存在') 158 | 159 | if admin.role_id != role.role_id and role.mark == default['role_mark']: 160 | return str('不能设为超级管理员') 161 | 162 | AllowableFields = ['nickname', 'sex', 'role_id', 'avatar', 'email', 'disable'] 163 | 164 | for i in params: 165 | if i in AllowableFields and hasattr(admin, i): 166 | setattr(admin, i, params[i]) 167 | 168 | if params['password'] != admin.password: 169 | admin.password = _config.get_md5(params['password']) 170 | 171 | user = copy.deepcopy(admin.to_json()) 172 | user['mark'] = role.mark 173 | # 删除不必要信息 174 | del_list = ['id', 'create_time', 'update_time'] 175 | for i in del_list: 176 | del user[i] 177 | 178 | s.commit() 179 | return user 180 | except Exception as e: 181 | print(e) 182 | s.rollback() 183 | return str(e) 184 | 185 | def LockAdminRequest(self, admin_id, disable): 186 | ''' 187 | 禁用管理员 188 | ''' 189 | s = db.session() 190 | try: 191 | s.query(Admin).filter(Admin.admin_id.in_(admin_id)).update({Admin.disable: disable}, synchronize_session=False) 192 | s.commit() 193 | return True 194 | except Exception as e: 195 | print(e) 196 | s.rollback() 197 | return str(e) 198 | 199 | def DelAdminRequest(self, admins): 200 | ''' 201 | 删除管理员 202 | ''' 203 | s = db.session() 204 | try: 205 | for admin in admins: 206 | result = s.query(Admin).filter(Admin.admin_id == admin).first() 207 | s.delete(result) 208 | s.commit() 209 | return True 210 | except Exception as e: 211 | print(e) 212 | s.rollback() 213 | return str(e) 214 | -------------------------------------------------------------------------------- /collection/v1/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2020-02-19 19:45:33 7 | LastEditTime: 2020-11-26 10:05:18 8 | LastEditors: Zpp 9 | ''' 10 | from models import db 11 | from models.system import Admin, Role, Menu, Interface, InitSql, Folder 12 | from models.log import Log 13 | from .interface import InterfaceModel 14 | from .menu import MenuModel 15 | from conf.setting import _config, init_menu, sql_dir, GeoLite2_dir, default, basedir 16 | from libs.utils import ReadFile, WriteFile 17 | from sqlalchemy import func, desc 18 | import geoip2.database 19 | import uuid 20 | import datetime 21 | import random 22 | import copy 23 | import os 24 | import time 25 | import calendar 26 | 27 | 28 | class BaseModel(): 29 | def __init__(self): 30 | self.role_name = '超级管理员' 31 | self.user_name = 'Admin' 32 | self.folder_name = '系统文件' 33 | self.M = MenuModel() 34 | self.I = InterfaceModel() 35 | 36 | def CreateDropRequest(self, is_init, params=None): 37 | db.session.remove() 38 | db.drop_all() 39 | db.create_all() 40 | 41 | s = db.session() 42 | try: 43 | role_id = str(uuid.uuid4()) 44 | role = Role( 45 | role_id=role_id, 46 | name=self.role_name, 47 | mark=default['role_mark'] 48 | ) 49 | 50 | self.__init_menus(role, s, init_menu) 51 | 52 | password = self.__get_code() 53 | if not is_init: 54 | admin = Admin( 55 | admin_id=str(uuid.uuid4()), 56 | username=self.user_name, 57 | password=_config.get_md5(password), 58 | role_id=role_id 59 | ) 60 | else: 61 | admin = Admin( 62 | admin_id=params['admin_id'], 63 | username=self.user_name, 64 | password=params['password'], 65 | role_id=role_id 66 | ) 67 | 68 | folder = Folder( 69 | folder_id=str(uuid.uuid4()), 70 | name=self.folder_name 71 | ) 72 | 73 | init = InitSql(is_init=True) 74 | 75 | s.add_all([role, admin, folder, init]) 76 | s.commit() 77 | return { 78 | 'username': self.user_name, 79 | 'password': password 80 | } 81 | except Exception as e: 82 | print(e) 83 | s.rollback() 84 | s.add(InitSql(is_init=False)) 85 | s.commit() 86 | return str(e) 87 | 88 | def __get_code(self): 89 | code_list = [] 90 | for i in range(10): # 0~9 91 | code_list.append(str(i)) 92 | for i in range(65, 91): # A-Z 93 | code_list.append(chr(i)) 94 | for i in range(97, 123): # a-z 95 | code_list.append(chr(i)) 96 | code = random.sample(code_list, 6) # 随机取6位数 97 | code_num = ''.join(code) 98 | return code_num 99 | 100 | def __init_menus(self, role, s, data, pid='0'): 101 | for m in data: 102 | menu_id = str(uuid.uuid4()) 103 | menu = self.__create_menu(role, m, menu_id, pid) 104 | 105 | if 'interface' in m: 106 | for f in m['interface']: 107 | is_exists = self.I.isCreateExists(s, f) 108 | if is_exists == True: 109 | interface = self.__create_interface(role, s, f, str(uuid.uuid4())) 110 | else: 111 | interface = is_exists['value'] 112 | s.add(interface) 113 | menu.interfaces.append(interface) 114 | 115 | s.add(menu) 116 | 117 | if 'children' in m: 118 | self.__init_menus(role, s, m['children'], menu_id) 119 | 120 | def __create_menu(self, role, params, menu_id, pid): 121 | return Menu( 122 | menu_id=menu_id, 123 | pid=pid, 124 | title=params['title'], 125 | path=params['path'], 126 | icon=params['icon'], 127 | mark=params['mark'], 128 | component=params['component'], 129 | componentPath=params['componentPath'], 130 | name=params['name'], 131 | cache=params['cache'], 132 | roles=[role] 133 | ) 134 | 135 | def __create_interface(self, role, s, params, interface_id): 136 | return Interface( 137 | interface_id=interface_id, 138 | name=params['name'], 139 | path=params['path'], 140 | method=params['method'], 141 | description=params['description'], 142 | mark=params['mark'], 143 | forbid=params['forbid'], 144 | roles=[role] 145 | ) 146 | 147 | def ExportSql(self, type=1): 148 | try: 149 | if not os.path.exists(sql_dir): 150 | os.mkdir(sql_dir) 151 | 152 | os.chdir(sql_dir) 153 | filename = 'TABLE%s.sql' % int(time.time() * 1000) 154 | 155 | sqlfromat = "%s -h%s -u%s -p%s -P%s %s >%s" 156 | if type == 2: 157 | sqlfromat = "%s -h%s -u%s -p%s -P%s -d %s >%s" # 不包含数据 158 | if type == 3: 159 | sqlfromat = "%s -h%s -u%s -p%s -P%s -t %s >%s" # 不包含表结构 160 | 161 | sql = (sqlfromat % ('mysqldump ', 162 | _config.host, 163 | _config.admin, 164 | _config.password, 165 | _config.port, 166 | _config.db, 167 | filename)) 168 | os.system(sql) 169 | return { 170 | 'path': os.path.join(sql_dir, filename), 171 | 'name': filename 172 | } 173 | except Exception as e: 174 | print(e) 175 | return str(e) 176 | 177 | def ImportSql(self, file): 178 | s = db.session() 179 | try: 180 | filename = 'TABLE%s.sql' % int(time.time() * 1000) 181 | config = _config 182 | 183 | if not os.path.exists(sql_dir): 184 | os.mkdir(sql_dir) 185 | 186 | file_path = os.path.join(sql_dir, filename) 187 | file.save(file_path) 188 | 189 | sqlfromat = "%s -h%s -u%s -p%s -P%s %s <%s" 190 | sql = (sqlfromat % ('mysql ', 191 | _config.host, 192 | _config.admin, 193 | _config.password, 194 | _config.port, 195 | _config.db, 196 | file_path)) 197 | 198 | db.drop_all() 199 | os.system(sql) 200 | return True 201 | except Exception as e: 202 | s.rollback() 203 | print(e) 204 | return str(e) 205 | 206 | def GetLoginInfo(self, admin_id, query_time): 207 | ''' 208 | 获取用户登录情况(根据用户名分组) 209 | ''' 210 | s = db.session() 211 | try: 212 | admin = s.query(Admin).filter(Admin.admin_id == admin_id).first() 213 | role = admin.role 214 | 215 | is_admin = False 216 | if role and role.mark == default['role_mark']: 217 | is_admin = True 218 | 219 | params = { 220 | 'type': 1, 221 | 'status': 0 222 | } 223 | if not is_admin: 224 | params['username'] = admin.username 225 | 226 | query_time = datetime.datetime.strptime(query_time, '%Y-%m') 227 | days = calendar.monthrange(query_time.year, query_time.month)[1] 228 | 229 | res = s.query( 230 | Log.username, 231 | func.date_format(Log.create_time, '%Y-%m-%d').label('date'), 232 | func.count('date') 233 | ).filter( 234 | Log.create_time.between( 235 | datetime.datetime(query_time.year, query_time.month, 1), 236 | datetime.datetime(query_time.year, query_time.month, days) 237 | ) 238 | ).filter_by(**params).group_by('date', 'username').all() 239 | 240 | data = {} 241 | for i in res: 242 | if i[1] not in data: 243 | data[i[1]] = [] 244 | data[i[1]].append({ 245 | 'name': i[0], 246 | 'count': i[2] 247 | }) 248 | 249 | return data 250 | except Exception as e: 251 | print(e) 252 | return str(e) 253 | 254 | def GetAllUserLoginCount(self): 255 | ''' 256 | 获取所有用户登录次数 257 | ''' 258 | s = db.session() 259 | try: 260 | user_count = s.query( 261 | Log.username, 262 | func.count(Log.username) 263 | ).filter( 264 | Log.type == 1, 265 | Log.status == 0 266 | ).group_by('username').all() 267 | 268 | user_list = ['用户', '登录次数'] 269 | data = [] 270 | for i in user_count: 271 | user_list.append(i[0]) 272 | data.append({ 273 | '用户': i[0], 274 | '登录次数': i[1] 275 | }) 276 | 277 | return { 278 | 'data': data, 279 | 'user': user_list 280 | } 281 | except Exception as e: 282 | print(e) 283 | return str(e) 284 | 285 | def GetUserLoginIp(self): 286 | ''' 287 | 获取用户登录IP分布情况 288 | ''' 289 | s = db.session() 290 | try: 291 | ip_list = s.query( 292 | Log.ip 293 | ).filter( 294 | Log.type == 1, 295 | Log.status == 0 296 | ).group_by('ip').all() 297 | 298 | reader = geoip2.database.Reader(GeoLite2_dir) 299 | 300 | ip = {} 301 | city = {} 302 | for i in ip_list: 303 | try: 304 | response = reader.city(i[0]) 305 | if response.city.names["zh-CN"] not in ip: 306 | ip[response.city.names["zh-CN"]] = { 307 | 'count': 1, 308 | 'ip': [i[0]] 309 | } 310 | else: 311 | ip[response.city.names["zh-CN"]]['count'] += 1 312 | ip[response.city.names["zh-CN"]]['ip'].append(i[0]) 313 | 314 | city[response.city.names["zh-CN"]] = [ 315 | response.location.longitude, 316 | response.location.latitude 317 | ] 318 | except: 319 | continue 320 | 321 | return { 322 | 'ip': ip, 323 | 'city': city 324 | } 325 | except Exception as e: 326 | print(e) 327 | return str(e) 328 | 329 | def GetReadmeContent(self): 330 | ''' 331 | 获取README.md的内容 332 | ''' 333 | try: 334 | path = os.path.join(basedir, 'README.md') 335 | content = ReadFile(path) 336 | 337 | return { 338 | 'content': content 339 | } 340 | except Exception as e: 341 | print(e) 342 | return str(e) 343 | -------------------------------------------------------------------------------- /collection/v1/document.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 文档控制器 5 | @Author: Zpp 6 | @Date: 2019-10-14 14:53:05 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-06-05 10:32:13 9 | ''' 10 | from flask import request 11 | from models import db 12 | from models.system import Document, Admin 13 | from conf.setting import document_dir 14 | from sqlalchemy import text, or_ 15 | import uuid 16 | import time 17 | import os 18 | 19 | 20 | class DocumentModel(): 21 | def QueryDocumentByParamRequest(self, params, page=1, page_size=20, order_by='create_time'): 22 | ''' 23 | 文档列表 24 | ''' 25 | s = db.session() 26 | try: 27 | deleted = Document.deleted == params['deleted'] 28 | folder = Document.folder_id == params['folder_id'] 29 | if params['folder_id'] == '0': 30 | folder = or_( 31 | Document.folder_id == params['folder_id'], 32 | Document.folder_id == None 33 | ) 34 | 35 | status = Document.status != 3 36 | if 'status' in params: 37 | status = Document.status == int(params['status']) 38 | 39 | result = s.query(Document, Admin.username)\ 40 | .filter(folder, deleted, status)\ 41 | .filter(Admin.admin_id == Document.admin_id)\ 42 | .order_by(order_by)\ 43 | .paginate(page, page_size, error_out=False) 44 | 45 | data = [] 46 | for i in result.items: 47 | value = i[0].to_json() 48 | value['username'] = i[1] 49 | data.append(value) 50 | 51 | return {'data': data, 'total': result.total} 52 | except Exception as e: 53 | print(e) 54 | return str(e) 55 | 56 | def file_extension(self, filename): 57 | ary = filename.split('.') 58 | count = len(ary) 59 | return ary[count - 1] if count > 1 else '' 60 | 61 | def allowed_file(self, file): 62 | ALLOWED_EXTENSIONS = set(['gif', 'jpeg', 'jpg', 'png', 'psd', 'bmp', 'tiff', 'tif', 63 | 'swc', 'iff', 'jpc', 'jp2', 'jpx', 'jb2', 'xbm', 'wbmp']) 64 | 65 | return '.' in file and file.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS 66 | 67 | def CreateDocumentRequest(self, files, params): 68 | ''' 69 | 新建文档 70 | ''' 71 | s = db.session() 72 | data = [] 73 | uids = params['uid[]'].split(',') 74 | for i, file in enumerate(files): 75 | # 上传 76 | file_name = file.filename 77 | ext = self.file_extension(file_name) 78 | size = len(file.read()) 79 | file_status = params['status'] 80 | if not self.allowed_file(file_name) and params['status'] != 3: 81 | file_status = 2 82 | 83 | try: 84 | file.seek(0) 85 | fn = '/' + str(time.strftime('%Y/%m/%d')) 86 | if not os.path.exists(document_dir + fn): 87 | os.makedirs(document_dir + fn) 88 | path = fn + '/' + str(uuid.uuid1()) + '.' + ext 89 | file.save(document_dir + path) 90 | 91 | item = Document( 92 | document_id=str(uuid.uuid4()), 93 | admin_id=params['admin_id'], 94 | name=file_name, 95 | status=file_status, 96 | ext=ext, 97 | path=path, 98 | size=size, 99 | folder_id=params['folder_id'] 100 | ) 101 | s.add(item) 102 | s.commit() 103 | data.append({ 104 | 'name': file_name, 105 | 'size': size, 106 | 'status': file_status, 107 | 'src': path, 108 | 'uid': uids[i], 109 | 'res': 1 110 | }) 111 | except Exception as e: 112 | print(e) 113 | s.rollback() 114 | data.append({ 115 | 'uid': uids[i], 116 | 'res': 2 117 | }) 118 | 119 | return data 120 | 121 | def GetDocumentRequest(self, document_id): 122 | ''' 123 | 查询文档 124 | ''' 125 | s = db.session() 126 | try: 127 | document = s.query(Document).filter(Document.document_id == document_id).first() 128 | if not document: 129 | return str('文档不存在') 130 | 131 | return document.to_json() 132 | except Exception as e: 133 | print(e) 134 | return str(e) 135 | 136 | def RetrieveDocument(self, document_id, deleted): 137 | ''' 138 | 移动文档到回收站 139 | ''' 140 | s = db.session() 141 | try: 142 | s.query(Document).filter(Document.document_id.in_(document_id)).update({Document.deleted: deleted}, synchronize_session=False) 143 | s.commit() 144 | return True 145 | except Exception as e: 146 | print(e) 147 | s.rollback() 148 | return str(e) 149 | 150 | def DelDocument(self, document_id): 151 | ''' 152 | 删除文档(包括服务器上文件) 153 | ''' 154 | s = db.session() 155 | try: 156 | res = s.query(Document).filter(Document.document_id.in_(document_id)) 157 | 158 | for i in res: 159 | s.delete(i) 160 | if os.path.exists(document_dir + i.path): 161 | os.remove(document_dir + i.path) 162 | s.commit() 163 | return True 164 | except Exception as e: 165 | print(e) 166 | s.rollback() 167 | return str(e) 168 | -------------------------------------------------------------------------------- /collection/v1/folder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 文件夹控制器 5 | @Author: Zpp 6 | @Date: 2019-12-23 14:53:50 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-26 09:32:22 9 | ''' 10 | from flask import request 11 | from models import db 12 | from models.system import Folder, Document 13 | from sqlalchemy import text, or_, and_ 14 | import uuid 15 | 16 | 17 | class FolderModel(): 18 | def QueryFolderByParamRequest(self, pid, admin_id): 19 | ''' 20 | 文件夹列表 21 | ''' 22 | s = db.session() 23 | try: 24 | result = Folder.query.filter( 25 | or_( 26 | and_( 27 | Folder.is_sys == True, 28 | Folder.pid == pid, 29 | ), 30 | and_( 31 | Folder.is_sys == False, 32 | Folder.pid == pid, 33 | Folder.admin_id == admin_id 34 | ) 35 | ) 36 | ).order_by(Folder.id).all() 37 | 38 | return [value.to_json() for value in result] 39 | except Exception as e: 40 | print(e) 41 | return str(e) 42 | 43 | def CreateFolderRequest(self, params): 44 | ''' 45 | 新建文件夹 46 | ''' 47 | s = db.session() 48 | try: 49 | item = Folder( 50 | folder_id=str(uuid.uuid4()), 51 | pid=params['pid'], 52 | name=params['name'], 53 | admin_id=params['admin_id'], 54 | is_sys=params['is_sys'] 55 | ) 56 | s.add(item) 57 | s.commit() 58 | return item.to_json() 59 | except Exception as e: 60 | s.rollback() 61 | print(e) 62 | return str(e) 63 | 64 | def ModifyFolderRequest(self, folder_id, params): 65 | ''' 66 | 修改文件夹 67 | ''' 68 | s = db.session() 69 | try: 70 | folder = s.query(Folder).filter(Folder.folder_id == folder_id).first() 71 | if not folder: 72 | return str('文件夹不存在') 73 | 74 | AllowableFields = ['pid', 'name'] 75 | 76 | for i in params: 77 | if i in AllowableFields and hasattr(folder, i): 78 | setattr(folder, i, params[i]) 79 | 80 | s.commit() 81 | return True 82 | except Exception as e: 83 | print(e) 84 | s.rollback() 85 | return str(e) 86 | 87 | def DelFolderRequest(self, folder_id): 88 | ''' 89 | 删除文件夹 90 | 关联文档默认到根目录 91 | ''' 92 | s = db.session() 93 | try: 94 | folder = s.query(Folder).filter(Folder.folder_id == folder_id).first() 95 | if not folder: 96 | return str('文件夹不存在') 97 | 98 | res = s.query(Folder).filter(Folder.pid == folder_id).all() 99 | for i in res: 100 | i.pid = '0' 101 | 102 | s.delete(folder) 103 | s.commit() 104 | return True 105 | except Exception as e: 106 | print(e) 107 | s.rollback() 108 | return str(e) 109 | -------------------------------------------------------------------------------- /collection/v1/interface.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 接口控制器 5 | @Author: Zpp 6 | @Date: 2019-10-14 13:40:29 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-26 16:00:42 9 | ''' 10 | from flask import request 11 | from models import db 12 | from models.system import Interface, Menu, Role 13 | from sqlalchemy import text 14 | from libs.scope import isExists 15 | import uuid 16 | import copy 17 | 18 | 19 | class InterfaceModel(): 20 | def __init__(self): 21 | self.exists = { 22 | 'name': '接口名称', 23 | 'path': '路由', 24 | 'mark': '标识' 25 | } 26 | 27 | def isCreateExists(self, s, params): 28 | ''' 29 | 判断记录是否存在 30 | ''' 31 | d = {} 32 | for i in self.exists: 33 | d[i] = { 34 | 'value': params[i], 35 | 'name': self.exists[i] 36 | } 37 | 38 | return isExists(s, Interface, d) 39 | 40 | def isSaveExists(self, s, params, data): 41 | ''' 42 | 判断修改时记录是否存在 43 | ''' 44 | d = {} 45 | for i in self.exists: 46 | if i in params and params[i] != data.__dict__[i]: 47 | d[i] = { 48 | 'value': params[i], 49 | 'name': self.exists[i] 50 | } 51 | 52 | return isExists(s, Interface, d) 53 | 54 | def QueryInterfaceByParamRequest(self, params, page=1, page_size=20, order_by='id'): 55 | ''' 56 | 接口列表 57 | ''' 58 | s = db.session() 59 | try: 60 | data = {} 61 | for i in ['disable', 'method']: 62 | if i in params: 63 | data[i] = params[i] 64 | 65 | menus = text('') 66 | if 'menu_id' in params: 67 | menus = Interface.menu.any(Menu.menu_id == params['menu_id']) 68 | 69 | roles = text('') 70 | if 'role_id' in params: 71 | roles = Interface.role.any(Role.role_id == params['role_id']) 72 | 73 | result = Interface.query.filter_by(**data).filter( 74 | Interface.name.like("%" + params['name'] + "%") if 'name' in params else text(''), 75 | menus, 76 | roles 77 | ).order_by(order_by).paginate(page, page_size, error_out=False) 78 | 79 | items = [] 80 | for i in result.items: 81 | menus = [{ 82 | 'name': m.title, 83 | 'menu_id': m.menu_id 84 | } for m in i.menus] 85 | roles = [{ 86 | 'name': r.name, 87 | 'role_id': r.role_id 88 | } for r in i.roles] 89 | item = i.to_json() 90 | item['menus'] = menus 91 | item['roles'] = roles 92 | items.append(item) 93 | 94 | return {'data': items, 'total': result.total} 95 | except Exception as e: 96 | print(e) 97 | return str(e) 98 | 99 | def CreateInterfaceRequest(self, params): 100 | ''' 101 | 新建接口 102 | ''' 103 | s = db.session() 104 | is_exists = self.isCreateExists(s, params) 105 | 106 | if is_exists != True: 107 | return str(is_exists['error'].encode('utf8')) 108 | 109 | try: 110 | menus = s.query(Menu).filter(Menu.menu_id.in_(params.getlist('menus[]'))).all() 111 | roles = s.query(Role).filter(Role.role_id.in_(params.getlist('roles[]'))).all() 112 | 113 | item = Interface( 114 | interface_id=str(uuid.uuid4()), 115 | name=params['name'], 116 | path=params['path'], 117 | method=params['method'], 118 | description=params['description'], 119 | mark=params['mark'], 120 | forbid=params['forbid'], 121 | disable=params['disable'], 122 | menus=menus, 123 | roles=roles 124 | ) 125 | 126 | s.add(item) 127 | data = copy.deepcopy(item.to_json()) 128 | s.commit() 129 | return data 130 | except Exception as e: 131 | s.rollback() 132 | print(e) 133 | return str(e) 134 | 135 | def ModifyInterfaceRequest(self, interface_id, params): 136 | ''' 137 | 修改接口信息 138 | ''' 139 | s = db.session() 140 | try: 141 | interface = s.query(Interface).filter(Interface.interface_id == interface_id).first() 142 | if not interface: 143 | return str('接口不存在') 144 | 145 | is_exists = self.isSaveExists(s, params, interface) 146 | 147 | if is_exists != True: 148 | return str(is_exists['error']) 149 | 150 | AllowableFields = ['name', 'path', 'method', 'description', 'disable'] 151 | 152 | for i in params: 153 | if i in AllowableFields and hasattr(interface, i): 154 | setattr(interface, i, params[i]) 155 | 156 | menus = s.query(Menu).filter(Menu.menu_id.in_(params.getlist('menus[]'))).all() 157 | roles = s.query(Role).filter(Role.role_id.in_(params.getlist('roles[]'))).all() 158 | interface.menus = menus 159 | interface.roles = roles 160 | s.commit() 161 | return True 162 | except Exception as e: 163 | print(e) 164 | s.rollback() 165 | return str(e) 166 | 167 | def LockInterfaceRequest(self, interface_id, disable): 168 | ''' 169 | 禁用接口 170 | ''' 171 | s = db.session() 172 | try: 173 | s.query(Interface).filter(Interface.interface_id.in_(interface_id)).update({Interface.disable: disable}, synchronize_session=False) 174 | s.commit() 175 | return True 176 | except Exception as e: 177 | print(e) 178 | s.rollback() 179 | return str(e) 180 | 181 | def DelInterfaceRequest(self, interface_id): 182 | ''' 183 | 删除接口 184 | ''' 185 | s = db.session() 186 | try: 187 | result = s.query(Interface).filter(Interface.interface_id.in_(interface_id)).all() 188 | for i in result: 189 | s.delete(i) 190 | s.commit() 191 | return True 192 | except Exception as e: 193 | print(e) 194 | s.rollback() 195 | return str(e) 196 | -------------------------------------------------------------------------------- /collection/v1/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 日志控制器 5 | @Author: Zpp 6 | @Date: 2019-10-17 14:53:00 7 | LastEditors: Zpp 8 | LastEditTime: 2022-04-11 13:28:43 9 | ''' 10 | from flask import request 11 | from models import db 12 | from models.log import Log 13 | from sqlalchemy import text 14 | import json 15 | 16 | 17 | class LogModel(): 18 | def QueryLogByParamRequest(self, params, page=1, page_size=20): 19 | ''' 20 | 日志列表 21 | ''' 22 | s = db.session() 23 | try: 24 | filters = { 25 | Log.type.in_(params['type']) if 'type' in params else text('1 = 1'), 26 | Log.status.in_(params['status']) if 'status' in params else text('1 = 1') 27 | } 28 | 29 | result = Log.query.filter(*filters).order_by(text('-id')).paginate(page, page_size, error_out=False) 30 | 31 | return {'data': [value.to_json() for value in result.items], 'total': result.total} 32 | except Exception as e: 33 | print(e) 34 | return str(e) 35 | 36 | def CreateLogRequest(self, params): 37 | ''' 38 | 新建日志 39 | ''' 40 | try: 41 | s = db.session() 42 | if not params['username']: 43 | return True 44 | 45 | t = 0 46 | if params['path'] == '/v1/Admin/Login': 47 | t = 1 48 | if str(params['content']) == '管理员被禁用': 49 | params['status'] = 2 50 | 51 | item = Log( 52 | ip=params['ip'], 53 | method=params['method'], 54 | path=params['path'], 55 | username=params['username'], 56 | status=params['status'], 57 | time=params['time'], 58 | content=str(params['content']), 59 | params=json.dumps(params['params']), 60 | type=t 61 | ) 62 | s.add(item) 63 | s.commit() 64 | return True 65 | except Exception as e: 66 | print(e) 67 | return False 68 | -------------------------------------------------------------------------------- /collection/v1/menu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-10 16:05:51 7 | LastEditTime: 2020-12-01 13:59:26 8 | LastEditors: Zpp 9 | ''' 10 | from flask import request 11 | from models import db 12 | from models.system import Menu, Role 13 | from conf.setting import default 14 | from libs.scope import isExists 15 | from sqlalchemy import text 16 | import uuid 17 | import json 18 | 19 | 20 | class MenuModel(): 21 | def __init__(self): 22 | self.exists = { 23 | 'name': '路由名称', 24 | 'title': '菜单名称', 25 | 'path': '路由', 26 | 'mark': '标识' 27 | } 28 | 29 | def isCreateExists(self, s, params): 30 | ''' 31 | 判断新增时记录是否存在 32 | ''' 33 | d = {} 34 | for i in self.exists: 35 | d[i] = { 36 | 'value': params[i], 37 | 'name': self.exists[i] 38 | } 39 | 40 | return isExists(s, Menu, d) 41 | 42 | def isSaveExists(self, s, params, data): 43 | ''' 44 | 判断修改时记录是否存在 45 | ''' 46 | d = {} 47 | for i in self.exists: 48 | if i in params and params[i] != data.__dict__[i]: 49 | d[i] = { 50 | 'value': params[i], 51 | 'name': self.exists[i] 52 | } 53 | 54 | return isExists(s, Menu, d) 55 | 56 | def QueryMenuByParamRequest(self, params, is_interface=False): 57 | ''' 58 | 菜单列表 59 | ''' 60 | s = db.session() 61 | try: 62 | data = {} 63 | if 'disable' in params: 64 | data['disable'] = params['disable'] 65 | 66 | result = Menu.query.filter_by(**data).order_by(Menu.sort, Menu.id).all() 67 | 68 | if is_interface: 69 | select = [] 70 | menus = [] 71 | 72 | for value in result: 73 | interfaces = [] 74 | for item in value.interfaces: 75 | interfaces.append({ 76 | 'type': 'INTERFACE', 77 | 'title': item.description, 78 | 'menu_id': '%s.%s' % (value.menu_id, item.interface_id) 79 | }) 80 | menus.append({ 81 | 'type': 'MENU', 82 | 'title': value.title, 83 | 'menu_id': value.menu_id, 84 | 'pid': value.pid, 85 | 'children': interfaces, 86 | 'count': Menu.query.filter(Menu.pid == value.menu_id).count() 87 | }) 88 | 89 | if 'role_id' in params: 90 | role = Role.query.filter(Role.role_id == params['role_id']).first() 91 | 92 | # vue树形控件的选择原理 只需要子节点id 93 | for i in role.interfaces: 94 | for m in i.menus: 95 | select.append('%s.%s' % (m.menu_id, i.interface_id)) 96 | # 补充没有接口的菜单 97 | menu = [i.menu_id for i in role.menus] 98 | for i in menus: 99 | if len(i['children']) == 0 and i['count'] == 0 and i['menu_id'] in menu: 100 | select.append(i['menu_id']) 101 | 102 | return { 103 | 'data': menus, 104 | 'select': select 105 | } 106 | else: 107 | res = [] 108 | for i in result: 109 | roles = [r.role_id for r in i.roles] 110 | item = i.to_json() 111 | item['roles'] = roles 112 | res.append(item) 113 | 114 | return res 115 | except Exception as e: 116 | print(e) 117 | return str(e) 118 | 119 | def CreateMenuRequest(self, params): 120 | ''' 121 | 新建菜单 122 | ''' 123 | s = db.session() 124 | is_exists = self.isCreateExists(s, params) 125 | 126 | if is_exists != True: 127 | return str(is_exists['error']) 128 | 129 | try: 130 | menu = Menu( 131 | menu_id=str(uuid.uuid4()), 132 | pid=params['pid'], 133 | title=params['title'], 134 | sort=int(params['sort']), 135 | path=params['path'], 136 | mark=params['mark'], 137 | icon=params['icon'], 138 | component=params['component'], 139 | componentPath=params['componentPath'], 140 | name=params['name'], 141 | cache=params['cache'], 142 | disable=params['disable'] 143 | ) 144 | 145 | # 角色关联 146 | res = s.query(Role).filter(Role.role_id.in_(params.getlist('roles[]'))).all() 147 | menu.roles = res 148 | s.add(menu) 149 | 150 | s.commit() 151 | return True 152 | except Exception as e: 153 | s.rollback() 154 | print(e) 155 | return str(e) 156 | 157 | def ModifyMenuRequest(self, menu_id, params): 158 | ''' 159 | 修改菜单信息 160 | ''' 161 | s = db.session() 162 | try: 163 | menu = s.query(Menu).filter(Menu.menu_id == menu_id).first() 164 | if not menu: 165 | return str('菜单不存在') 166 | 167 | is_exists = self.isSaveExists(s, params, menu) 168 | 169 | if is_exists != True: 170 | return str(is_exists['error']) 171 | 172 | AllowableFields = ['pid', 'title', 'path', 'icon', 'sort', 'component', 'componentPath', 'name', 'cache', 'disable'] 173 | 174 | for i in params: 175 | if i in AllowableFields and hasattr(menu, i): 176 | setattr(menu, i, params[i]) 177 | 178 | # 角色关联 179 | res = s.query(Role).filter(Role.role_id.in_(params.getlist('roles[]'))).all() 180 | menu.roles = res 181 | 182 | s.commit() 183 | return True 184 | except Exception as e: 185 | print(e) 186 | s.rollback() 187 | return str(e) 188 | 189 | def DelMenuRequest(self, menu_id): 190 | ''' 191 | 删除菜单 192 | ''' 193 | s = db.session() 194 | try: 195 | menu = s.query(Menu).filter(Menu.menu_id == menu_id).first() 196 | s.delete(menu) 197 | 198 | # 子菜单移动到根目录 199 | s.query(Menu).filter(Menu.pid == menu_id).update({ 200 | 'pid': '0' 201 | }) 202 | s.commit() 203 | return True 204 | except Exception as e: 205 | print(e) 206 | s.rollback() 207 | return str(e) 208 | 209 | def GetMenuToInterfaceRequest(self, menu_id): 210 | ''' 211 | 获取菜单下级联的API接口 212 | ''' 213 | s = db.session() 214 | try: 215 | menu = s.query(Menu).filter(Menu.menu_id == menu_id).first() 216 | return [i.to_json() for i in menu.interfaces] 217 | except Exception as e: 218 | print(e) 219 | s.rollback() 220 | return str(e) 221 | -------------------------------------------------------------------------------- /collection/v1/role.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 鉴权控制器 5 | @Author: Zpp 6 | @Date: 2019-09-10 16:01:46 7 | LastEditTime: 2020-11-26 13:49:01 8 | LastEditors: Zpp 9 | ''' 10 | from flask import request 11 | from models import db 12 | from models.system import Role, Interface, Menu 13 | from sqlalchemy import text 14 | from libs.scope import isExists 15 | from conf.setting import default 16 | import uuid 17 | import json 18 | 19 | 20 | class RoleModel(): 21 | def __init__(self): 22 | self.exists = { 23 | 'name': '角色名称', 24 | 'mark': '标识' 25 | } 26 | 27 | def isCreateExists(self, s, params): 28 | ''' 29 | 判断新增时记录是否存在 30 | ''' 31 | d = {} 32 | for i in self.exists: 33 | d[i] = { 34 | 'value': params[i], 35 | 'name': self.exists[i] 36 | } 37 | 38 | return isExists(s, Role, d) 39 | 40 | def isSaveExists(self, s, params, data): 41 | ''' 42 | 判断修改时记录是否存在 43 | ''' 44 | d = {} 45 | for i in self.exists: 46 | if i in params and params[i] != data.__dict__[i]: 47 | d[i] = { 48 | 'value': params[i], 49 | 'name': self.exists[i] 50 | } 51 | 52 | return isExists(s, Role, d) 53 | 54 | def CreateRoleRequest(self, params): 55 | ''' 56 | 新建鉴权 57 | ''' 58 | s = db.session() 59 | is_exists = self.isCreateExists(s, params) 60 | 61 | if is_exists != True: 62 | return str(is_exists['error']) 63 | 64 | try: 65 | item = Role( 66 | name=params['name'], 67 | mark=params['mark'], 68 | role_id=str(uuid.uuid4()), 69 | disable=params['disable'], 70 | menus=s.query(Menu).filter(Menu.menu_id.in_(params.getlist('menu[]'))).all(), 71 | interfaces=s.query(Interface).filter(Interface.interface_id.in_(params.getlist('interface[]'))).all() 72 | ) 73 | s.add(item) 74 | s.commit() 75 | return True 76 | except Exception as e: 77 | s.rollback() 78 | print(e) 79 | return str(e) 80 | 81 | def GetRoleRequest(self, role_id): 82 | ''' 83 | 查询鉴权 84 | ''' 85 | s = db.session() 86 | try: 87 | role = s.query(Role).filter(Role.role_id == role_id).first() 88 | if not role: 89 | return str('数据不存在') 90 | 91 | return role.to_json() 92 | except Exception as e: 93 | print(e) 94 | return str(e) 95 | 96 | def ModifyRoleRequest(self, role_id, params): 97 | ''' 98 | 修改鉴权信息 99 | ''' 100 | s = db.session() 101 | try: 102 | role = s.query(Role).filter(Role.role_id == role_id).first() 103 | if not role: 104 | return str('数据不存在') 105 | 106 | is_exists = self.isSaveExists(s, params, role) 107 | 108 | if is_exists != True: 109 | return str(is_exists['error']) 110 | 111 | role.name = params['name'] 112 | role.mark = params['mark'] 113 | role.disable = params['disable'] 114 | role.menus = s.query(Menu).filter(Menu.menu_id.in_(params.getlist('menu[]'))).all() 115 | role.interfaces = s.query(Interface).filter(Interface.interface_id.in_(params.getlist('interface[]'))).all() 116 | s.commit() 117 | return True 118 | except Exception as e: 119 | print(e) 120 | s.rollback() 121 | return str(e) 122 | 123 | def LockRoleRequest(self, role_id, disable): 124 | ''' 125 | 禁用鉴权 126 | ''' 127 | s = db.session() 128 | try: 129 | s.query(Role).filter(Role.role_id.in_(role_id)).update({Role.disable: disable}, synchronize_session=False) 130 | s.commit() 131 | return True 132 | except Exception as e: 133 | print(e) 134 | s.rollback() 135 | return str(e) 136 | 137 | def DelRoleRequest(self, role_id): 138 | ''' 139 | 删除鉴权 140 | ''' 141 | s = db.session() 142 | try: 143 | role = s.query(Role).filter(Role.role_id.in_(role_id)).all() 144 | for i in role: 145 | s.delete(i) 146 | s.commit() 147 | return True 148 | except Exception as e: 149 | print(e) 150 | s.rollback() 151 | return str(e) 152 | 153 | def QueryRoleByParamRequest(self, params): 154 | ''' 155 | 鉴权列表 156 | ''' 157 | s = db.session() 158 | try: 159 | data = {} 160 | if 'disable' in params: 161 | data['disable'] = params['disable'] 162 | 163 | result = Role.query.filter_by(**data).order_by(Role.id).all() 164 | 165 | if 'is_default' in params: 166 | res = Role.query.filter(Role.mark == default['role_mark']).first() 167 | 168 | return { 169 | 'data': [value.to_json() for value in result], 170 | 'default': res.role_id if res else None 171 | } 172 | else: 173 | return [value.to_json() for value in result] 174 | except Exception as e: 175 | print(e) 176 | return str(e) 177 | -------------------------------------------------------------------------------- /conf/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-02 16:04:11 7 | @LastEditTime: 2019-09-12 11:27:19 8 | @LastEditors: Zpp 9 | ''' 10 | -------------------------------------------------------------------------------- /conf/setting.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 基本配置信息 5 | @Author: Zpp 6 | @Date: 2019-09-02 15:53:39 7 | LastEditTime: 2022-03-22 08:56:30 8 | LastEditors: Zpp 9 | ''' 10 | import hashlib 11 | import os 12 | from libs.utils import IsWindows 13 | 14 | basedir = os.path.abspath(os.path.dirname(__file__) + '/..') 15 | 16 | base_info = { 17 | 'lock_times': 5, # 开始锁定的次数 18 | 'lock_every_time': 5 # 超过锁定次数每次加的时间 19 | } 20 | 21 | token_info = { 22 | 'expiration': 30 * 24 * 3600, 23 | 'SECRET_KEY': 'k#6@1%8)a' 24 | } 25 | 26 | # 启动服务参数 字典类型 27 | server_info = { 28 | "host": '0.0.0.0', 29 | "port": 92, # 启动服务的端口号 30 | } 31 | 32 | # 文档路径 33 | document_dir = os.path.join(basedir, 'document') 34 | 35 | # excel存放路径 36 | excel_dir = os.path.join(basedir, 'excel') 37 | 38 | # GeoLite2路径 39 | GeoLite2_dir = os.path.join(basedir, 'tools/GeoLite2-City.mmdb') 40 | 41 | # sql下载临时路径 42 | sql_dir = os.path.join(basedir, 'sql') 43 | 44 | # 日志 45 | log_info = { 46 | 'LOG_FILE_BACKUP_COUNT': 0, 47 | 'LOG_PATH_INFO': os.path.join(os.path.join(basedir, 'logs'), 'info.log') 48 | } 49 | 50 | # session参数 51 | session_info = { 52 | 'SESSION_TYPE': 'filesystem', 53 | 'SESSION_FILE_DIR': os.path.join(basedir, 'sessions'), 54 | 'SESSION_FILE_THRESHOLD': 500, 55 | 'SESSION_FILE_MODE': 384, 56 | 'SESSION_PERMANENT': True 57 | } 58 | 59 | # cache参数 60 | cache_info = { 61 | 'CACHE_TYPE': 'filesystem', 62 | 'CACHE_DIR': os.path.join(basedir, 'caches'), 63 | 'CACHE_THRESHOLD': 500 64 | } 65 | 66 | # 初始化菜单和下属的接口 67 | init_menu = [ 68 | { 69 | "title": "系统", 70 | "path": "/system", 71 | "icon": "cog", 72 | "mark": "system", 73 | 'component': 'layoutHeaderAside', 74 | 'componentPath': 'layout/header-aside', 75 | 'name': 'System', 76 | 'cache': True, 77 | "children": [ 78 | { 79 | "title": "菜单管理", 80 | "path": "/system/menu", 81 | "icon": "th-list", 82 | "mark": "system_menu", 83 | 'name': 'MenuPage', 84 | 'component': 'menu', 85 | 'componentPath': 'sys/menu/index', 86 | 'cache': True, 87 | "interface": [ 88 | { 89 | "path": "/v1/Menu/CreateMenu", 90 | "method": "POST", 91 | "name": "CreateMenu", 92 | "description": "添加菜单", 93 | "mark": "add_menu", 94 | "forbid": False 95 | }, 96 | { 97 | "path": "/v1/Menu/QueryMenuByParam", 98 | "method": "POST", 99 | "name": "QueryMenuByParam", 100 | "description": "获取菜单列表", 101 | "mark": "list_menu", 102 | "forbid": True 103 | }, 104 | { 105 | "path": "/v1/Menu/ModifyMenu", 106 | "method": "POST", 107 | "name": "ModifyMenu", 108 | "description": "修改菜单信息", 109 | "mark": "set_menu", 110 | "forbid": False 111 | }, 112 | { 113 | "path": "/v1/Menu/DelMenu", 114 | "method": "POST", 115 | "name": "DelMenu", 116 | "description": "删除菜单", 117 | "mark": "del_menu", 118 | "forbid": False 119 | }, 120 | { 121 | "path": "/v1/Menu/GetMenuToInterface", 122 | "method": "GET", 123 | "name": "GetMenuToInterface", 124 | "description": "获取菜单下级联的API接口", 125 | "mark": "interface_menu", 126 | "forbid": False 127 | } 128 | ] 129 | }, 130 | { 131 | "title": "接口管理", 132 | "path": "/system/interface", 133 | "icon": "send", 134 | "mark": "system_interface", 135 | 'name': 'InterfacePage', 136 | 'component': 'interface', 137 | 'componentPath': 'sys/interface/index', 138 | 'cache': True, 139 | "interface": [ 140 | { 141 | "path": "/v1/Interface/CreateInterface", 142 | "method": "POST", 143 | "name": "CreateInterface", 144 | "description": "添加接口", 145 | "mark": "add_interface", 146 | "forbid": False 147 | }, 148 | { 149 | "path": "/v1/Interface/QueryInterfaceByParam", 150 | "method": "POST", 151 | "name": "QueryInterfaceByParam", 152 | "description": "获取接口列表", 153 | "mark": "list_interface", 154 | "forbid": True 155 | }, 156 | { 157 | "path": "/v1/Interface/ModifyInterface", 158 | "method": "POST", 159 | "name": "ModifyInterface", 160 | "description": "修改接口信息", 161 | "mark": "set_interface", 162 | "forbid": False 163 | }, 164 | { 165 | "path": "/v1/Interface/LockInterface", 166 | "method": "POST", 167 | "name": "LockInterface", 168 | "description": "禁用接口", 169 | "mark": "lock_interface", 170 | "forbid": False 171 | }, 172 | { 173 | "path": "/v1/Interface/DelInterface", 174 | "method": "POST", 175 | "name": "DelInterface", 176 | "description": "删除接口", 177 | "mark": "del_interface", 178 | "forbid": False 179 | } 180 | ] 181 | }, 182 | { 183 | "title": "附件管理", 184 | "path": "/system/document", 185 | "icon": "folder-open-o", 186 | "mark": "system_file", 187 | 'name': 'DocumentPage', 188 | 'component': 'document', 189 | 'componentPath': 'sys/document/index', 190 | 'cache': True, 191 | "interface": [ 192 | { 193 | "path": "/v1/Document/CreateDocument", 194 | "method": "POST", 195 | "name": "CreateDocument", 196 | "description": "添加附件", 197 | "mark": "add_document", 198 | "forbid": False 199 | }, 200 | { 201 | "path": "/v1/Document/QueryDocumentByParam", 202 | "method": "POST", 203 | "name": "QueryDocumentByParam", 204 | "description": "获取附件列表", 205 | "mark": "list_document", 206 | "forbid": True 207 | }, 208 | { 209 | "path": "/v1/Document/DownDocument", 210 | "method": "GET", 211 | "name": "DownDocument", 212 | "description": "下载附件", 213 | "mark": "down_document", 214 | "forbid": False 215 | }, 216 | { 217 | "path": "/v1/Document/DelDocument", 218 | "method": "POST", 219 | "name": "DelDocument", 220 | "description": "删除附件", 221 | "mark": "del_document", 222 | "forbid": False 223 | }, 224 | { 225 | "path": "/v1/Document/RetrieveDocument", 226 | "method": "POST", 227 | "name": "RetrieveDocument", 228 | "description": "回收附件", 229 | "mark": "retrieve_document", 230 | "forbid": False 231 | }, 232 | { 233 | "path": "/v1/Folder/CreateFolder", 234 | "method": "POST", 235 | "name": "CreateFolder", 236 | "description": "创建文件夹", 237 | "mark": "addf_folder", 238 | "forbid": False 239 | }, 240 | { 241 | "path": "/v1/Folder/DelFolder", 242 | "method": "POST", 243 | "name": "DelFolder", 244 | "description": "删除文件夹", 245 | "mark": "delf_folder", 246 | "forbid": False 247 | }, 248 | { 249 | "path": "/v1/Folder/ModifyFolder", 250 | "method": "POST", 251 | "name": "ModifyFolder", 252 | "description": "修改文件夹", 253 | "mark": "setf_folder", 254 | "forbid": False 255 | }, 256 | { 257 | "path": "/v1/Folder/QueryFolderByParam", 258 | "method": "POST", 259 | "name": "QueryFolderByParam", 260 | "description": "获取文件列表", 261 | "mark": "listf_folder", 262 | "forbid": True 263 | } 264 | ] 265 | }, 266 | { 267 | "title": "数据库管理", 268 | "path": "/system/base", 269 | "icon": "database", 270 | "mark": "system_base", 271 | 'name': 'BasePage', 272 | 'component': 'base', 273 | 'componentPath': 'sys/base/index', 274 | 'cache': True, 275 | "interface": [ 276 | { 277 | "path": "/v1/Base/ExportSql", 278 | "method": "POST", 279 | "name": "ExportSql", 280 | "description": "导出数据库", 281 | "mark": "export_sql", 282 | "forbid": False 283 | }, 284 | { 285 | "path": "/v1/Base/GetLoginInfo", 286 | "method": "POST", 287 | "name": "GetLoginInfo", 288 | "description": "获取用户登录情况", 289 | "mark": "get_login_info", 290 | "forbid": False 291 | }, 292 | { 293 | "path": "/v1/Base/GetAllUserLoginCount", 294 | "method": "POST", 295 | "name": "GetAllUserLoginCount", 296 | "description": "获取所有用户登录次数", 297 | "mark": "get_all_user_login_count", 298 | "forbid": False 299 | }, 300 | { 301 | "path": "/v1/Base/GetUserLoginIp", 302 | "method": "POST", 303 | "name": "GetUserLoginIp", 304 | "description": "获取用户登录IP分布情况", 305 | "mark": "get_user_ip", 306 | "forbid": False 307 | } 308 | ] 309 | } 310 | ] 311 | }, 312 | { 313 | "title": "鉴权", 314 | "path": "/role", 315 | "icon": "shield", 316 | "mark": "role", 317 | 'name': 'Role', 318 | 'component': 'layoutHeaderAside', 319 | 'componentPath': 'layout/header-aside', 320 | 'cache': True, 321 | "children": [ 322 | { 323 | "title": "管理员用户", 324 | "path": "/role/admin", 325 | "icon": "user", 326 | "mark": "role_admin", 327 | 'name': 'AdminPage', 328 | 'component': 'admin', 329 | 'componentPath': 'sys/admin/index', 330 | 'cache': True, 331 | "interface": [ 332 | { 333 | "path": "/v1/Admin/CreateAdmin", 334 | "method": "POST", 335 | "name": "CreateAdmin", 336 | "description": "注册管理员", 337 | "mark": "add_admin", 338 | "forbid": False 339 | }, 340 | { 341 | "path": "/v1/Admin/QueryAdminByParam", 342 | "method": "POST", 343 | "name": "QueryAdminByParam", 344 | "description": "获取管理员列表", 345 | "mark": "list_admin", 346 | "forbid": True 347 | }, 348 | { 349 | "path": "/v1/Admin/ModifyAdmin", 350 | "method": "POST", 351 | "name": "ModifyAdmin", 352 | "description": "修改管理员信息", 353 | "mark": "set_admin", 354 | "forbid": False 355 | }, 356 | { 357 | "path": "/v1/Admin/LockAdmin", 358 | "method": "POST", 359 | "name": "LockAdmin", 360 | "description": "禁用管理员", 361 | "mark": "lock_admin", 362 | "forbid": False 363 | }, 364 | { 365 | "path": "/v1/Admin/DelAdmin", 366 | "method": "POST", 367 | "name": "DelAdmin", 368 | "description": "删除管理员", 369 | "mark": "del_admin", 370 | "forbid": False 371 | } 372 | ] 373 | }, 374 | { 375 | "title": "角色管理", 376 | "path": "/role/role", 377 | "icon": "group", 378 | "mark": "role_group", 379 | 'name': 'RolePage', 380 | 'component': 'role', 381 | 'componentPath': 'sys/role/index', 382 | 'cache': True, 383 | "interface": [ 384 | { 385 | "path": "/v1/Role/CreateRole", 386 | "method": "POST", 387 | "name": "CreateRole", 388 | "description": "添加角色", 389 | "mark": "add_role", 390 | "forbid": False 391 | }, 392 | { 393 | "path": "/v1/Role/QueryRoleByParam", 394 | "method": "POST", 395 | "name": "QueryRoleByParam", 396 | "description": "获取角色列表", 397 | "mark": "list_role", 398 | "forbid": True 399 | }, 400 | { 401 | "path": "/v1/Role/ModifyRole", 402 | "method": "POST", 403 | "name": "ModifyRole", 404 | "description": "修改角色信息", 405 | "mark": "set_role", 406 | "forbid": False 407 | }, 408 | { 409 | "path": "/v1/Role/LockRole", 410 | "method": "POST", 411 | "name": "LockRole", 412 | "description": "禁用角色", 413 | "mark": "lock_role", 414 | "forbid": False 415 | }, 416 | { 417 | "path": "/v1/Role/DelRole", 418 | "method": "POST", 419 | "name": "DelRole", 420 | "description": "删除角色", 421 | "mark": "del_role", 422 | "forbid": False 423 | } 424 | ] 425 | } 426 | ] 427 | }, 428 | { 429 | "title": "日志", 430 | "path": "/log", 431 | "icon": "bullseye", 432 | "mark": "log", 433 | 'name': 'Log', 434 | 'component': 'layoutHeaderAside', 435 | 'componentPath': 'layout/header-aside', 436 | 'cache': True, 437 | "children": [ 438 | { 439 | "title": "登录日志", 440 | "path": "/log/login", 441 | "icon": "street-view", 442 | "mark": "log_login", 443 | 'name': 'LogLogin', 444 | 'component': 'login', 445 | 'componentPath': 'sys/login/index', 446 | 'cache': True, 447 | "interface": [ 448 | { 449 | "path": "/v1/Log/QueryLogByParam", 450 | "method": "POST", 451 | "name": "QueryLogByParam", 452 | "description": "获取日志列表", 453 | "mark": "get_log_list", 454 | "forbid": True 455 | } 456 | ] 457 | }, 458 | { 459 | "title": "操作日志", 460 | "path": "/log/hander", 461 | "mark": "log_hander", 462 | "icon": "dot-circle-o", 463 | 'name': 'LogHander', 464 | 'component': 'hander', 465 | 'componentPath': 'sys/hander/index', 466 | 'cache': True, 467 | "interface": [ 468 | { 469 | "path": "/v1/Log/QueryLogByParam", 470 | "method": "POST", 471 | "name": "QueryLogByParam", 472 | "description": "获取日志列表", 473 | "mark": "get_log_list", 474 | "forbid": True 475 | } 476 | ] 477 | }, 478 | { 479 | "title": "异常日志", 480 | "path": "/log/error", 481 | "mark": "log_error", 482 | "icon": "bug", 483 | 'name': 'LorError', 484 | 'component': 'error', 485 | 'componentPath': 'sys/error/index', 486 | 'cache': True, 487 | "interface": [ 488 | { 489 | "path": "/v1/Log/QueryLogByParam", 490 | "method": "POST", 491 | "name": "QueryLogByParam", 492 | "description": "获取日志列表", 493 | "mark": "get_log_list", 494 | "forbid": True 495 | } 496 | ] 497 | } 498 | ] 499 | }, 500 | { 501 | "title": "Markdown", 502 | "path": "/markdown", 503 | "icon": "file-text-o", 504 | "mark": "markdown", 505 | 'name': 'Markdown', 506 | 'component': 'layoutHeaderAside', 507 | 'componentPath': 'layout/header-aside', 508 | 'cache': True, 509 | "children": [ 510 | { 511 | "title": "markdown编辑器", 512 | "path": "/markdown/wirte", 513 | "icon": "edit", 514 | "mark": "markdown_wirte", 515 | 'name': 'MarkdwonWrite', 516 | 'component': 'markdown_wirte', 517 | 'componentPath': 'sys/markdown/wirte', 518 | 'cache': True 519 | }, 520 | { 521 | "title": "预览", 522 | "path": "/markdown/preview", 523 | "mark": "markdown_preview", 524 | "icon": "eye", 525 | 'name': 'Preview', 526 | 'component': 'preview', 527 | 'componentPath': 'sys/markdown/index', 528 | 'cache': True, 529 | "interface": [ 530 | { 531 | "path": "/v1/Base/GetReadmeContent", 532 | "method": "POST", 533 | "name": "GetReadmeContent", 534 | "description": "获取README.md的内容", 535 | "mark": "get_content", 536 | "forbid": True 537 | } 538 | ] 539 | } 540 | ] 541 | } 542 | ] 543 | 544 | 545 | # 默认判断字段 546 | default = { 547 | 'role_mark': 'SYS_ADMIN' 548 | } 549 | 550 | 551 | class Config(): 552 | def __init__(self): 553 | # mysql 配置信息 554 | self.host = '127.0.0.1' 555 | self.charset = 'utf8' 556 | if IsWindows(): 557 | self.port = 3306 558 | self.admin = 'root' 559 | self.password = 'intersky' 560 | self.db = 'chubby' 561 | else: 562 | self.port = 3306 563 | self.admin = 'admin_zpp' 564 | self.password = 'Zt931210' 565 | self.db = 'admin' 566 | 567 | def get_sql_url(self): 568 | return "mysql://%s:%s@%s:%s/%s?charset=utf8" % (self.admin, self.password, self.host, self.port, self.db) 569 | 570 | def get_md5(self, m): 571 | h = hashlib.md5() 572 | h.update(m.encode('utf-8')) 573 | return h.hexdigest() 574 | 575 | 576 | _config = Config() 577 | -------------------------------------------------------------------------------- /libs/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-04 16:50:59 7 | @LastEditTime: 2019-12-09 15:35:28 8 | @LastEditors: Zpp 9 | ''' 10 | -------------------------------------------------------------------------------- /libs/aliyun.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 阿里云短信 5 | @Author: Zpp 6 | @Date: 2020-04-13 10:30:25 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-04-15 09:43:58 9 | ''' 10 | 11 | from aliyunsdkcore.client import AcsClient 12 | from aliyunsdkcore.request import CommonRequest 13 | from conf.aliyun import ali_info 14 | from flask import session 15 | import json 16 | import string 17 | import random 18 | import datetime 19 | 20 | 21 | class AliyunModel(): 22 | def get_code(self): 23 | return "".join(random.sample(string.digits, 4)) 24 | 25 | def SmsSend(self, phone): 26 | code = self.get_code() 27 | client = AcsClient(ali_info['AccessKey'], ali_info['Secret'], 'cn-hangzhou') 28 | request = CommonRequest() 29 | request.set_accept_format('json') 30 | request.set_domain('dysmsapi.aliyuncs.com') 31 | request.set_method('POST') 32 | request.set_protocol_type('https') # https | http 33 | request.set_version('2017-05-25') 34 | request.set_action_name('SendSms') 35 | 36 | request.add_query_param('RegionId', "cn-hangzhou") 37 | request.add_query_param('PhoneNumbers', phone) 38 | request.add_query_param('SignName', ali_info['SignName']) 39 | request.add_query_param('TemplateCode', ali_info['TemplateCode']) 40 | request.add_query_param('TemplateParam', json.dumps({ 41 | 'code': code 42 | })) 43 | 44 | response = json.loads(client.do_action(request)) 45 | 46 | if response['Code'] == 'OK': 47 | session['Code'] = { 48 | 'code': code, 49 | 'time': datetime.datetime.now() 50 | } 51 | return True 52 | else: 53 | return str(response['Message']) 54 | 55 | -------------------------------------------------------------------------------- /libs/captcha.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 验证码 5 | @Author: Zpp 6 | @Date: 2019-09-17 15:07:00 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-07 10:52:52 9 | ''' 10 | import random 11 | import string 12 | 13 | """ 14 | Image: 画布 15 | ImageDraw: 画笔 16 | ImageFont: 字体 17 | """ 18 | from PIL import Image, ImageDraw, ImageFont 19 | from .utils import IsWindows 20 | 21 | 22 | class Captcha(object): 23 | # 生成的验证码的个数 24 | number = 4 25 | # 图片的宽度和高度 26 | size = (90, 38) 27 | # 字体大小 28 | fontsize = 25 29 | # 干扰线条数 30 | line_number = 2 31 | 32 | SOURCE = list(string.ascii_letters) 33 | SOURCE.extend(list(map(str, list(range(0, 10))))) 34 | 35 | @classmethod 36 | def __gen_line(cls, draw, width, height): 37 | """ 38 | 绘制干扰线 39 | """ 40 | begin = (random.randint(0, width), random.randint(0, height)) 41 | end = (random.randint(0, width), random.randint(0, height)) 42 | draw.line([begin, end], fill=cls.__gen_random_color(), width=2) 43 | 44 | @classmethod 45 | def __gen_random_color(cls, start=0, end=255): 46 | """ 47 | 产生随机颜色 48 | 颜色的取值范围是0~255 49 | """ 50 | random.seed() 51 | return ( 52 | random.randint(start, end), 53 | random.randint(start, end), 54 | random.randint(start, end), 55 | ) 56 | 57 | @classmethod 58 | def __gen_points(cls, draw, point_chance, width, height): 59 | """ 60 | 绘制干扰点 61 | """ 62 | chance = min(100, max(0, int(point_chance))) 63 | for w in range(width): 64 | for h in range(height): 65 | temp = random.randint(0, 100) 66 | if temp > 100 - chance: 67 | draw.point((w, h), fill=cls.__gen_random_color()) 68 | 69 | @classmethod 70 | def __gen_random_font(cls): 71 | """ 72 | 采用随机字体 73 | :return: 74 | """ 75 | fonts = ["consola.ttf", "consolab.ttf", "consolai.ttf"] 76 | font = random.choice(fonts) 77 | return ("utils/captcha/" if IsWindows() else "/home/") + font 78 | 79 | @classmethod 80 | def gen_text(cls, number): 81 | """ 82 | 随机生成一个字符串 83 | :param number: 字符串数量 84 | """ 85 | return "".join(random.sample(cls.SOURCE, number)) 86 | 87 | @classmethod 88 | def gen_graph_captcha(cls): 89 | width, height = cls.size 90 | # A表示透明度 91 | image = Image.new("RGBA", (width, height), cls.__gen_random_color(0, 100)) 92 | # 字体 93 | font = ImageFont.truetype(cls.__gen_random_font(), cls.fontsize) 94 | # 创建画笔 95 | draw = ImageDraw.Draw(image) 96 | # 生成随机字符串 97 | text = cls.gen_text(cls.number) 98 | # 字体大小 99 | font_width, font_height = font.getsize(text) 100 | # 填充字符串 101 | draw.text( 102 | ((width - font_width) / 2, (height - font_height) / 2), 103 | text, 104 | font=font, 105 | fill=cls.__gen_random_color(150, 255), 106 | ) 107 | # 绘制干扰线 108 | for x in range(0, cls.line_number): 109 | cls.__gen_line(draw, width, height) 110 | # 绘制噪点 111 | cls.__gen_points(draw, 10, width, height) 112 | 113 | return text, image 114 | -------------------------------------------------------------------------------- /libs/code.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: response返回处理方法 5 | @Author: Zpp 6 | @Date: 2019-09-04 17:09:14 7 | LastEditTime: 2020-11-24 16:54:52 8 | LastEditors: Zpp 9 | ''' 10 | from flask import jsonify, request, session 11 | from collection.v1.log import LogModel 12 | from datetime import datetime 13 | import json 14 | import time 15 | 16 | 17 | class MyEncoder(json.JSONEncoder): 18 | def default(self, obj): 19 | if isinstance(obj, bytes): 20 | return str(obj, encoding='utf-8') 21 | return json.JSONEncoder.default(self, obj) 22 | 23 | 24 | def ResultDeal(code=0, data={}, msg=''): 25 | response = jsonify(json.loads(json.dumps({ 26 | 'code': code, 27 | 'data': data, 28 | 'msg': msg 29 | }, cls=MyEncoder))) 30 | 31 | if response.headers['Content-Type'] == 'application/json' and response.status_code == 200: 32 | params = { 33 | 'ip': request.headers['X-Real-Ip'] if 'X-Real-Ip' in request.headers else request.remote_addr, 34 | 'method': request.method, 35 | 'path': request.path, 36 | 'username': session.get('username'), 37 | 'time': GetTimestamp() - session.get('requestTime'), 38 | 'params': request.values.to_dict() 39 | } 40 | 41 | body = json.loads(response.data) 42 | if body['code'] == 0: 43 | params['status'] = 0 44 | params['content'] = '' 45 | LogModel().CreateLogRequest(params) 46 | if body['code'] == -1: 47 | params['status'] = 1 48 | params['content'] = body['msg'] 49 | LogModel().CreateLogRequest(params) 50 | 51 | return response 52 | 53 | 54 | def GetTimestamp(): 55 | now = time.time() 56 | local_time = time.localtime(now) 57 | date = str(time.strftime("%Y%m%d%H%M%S", local_time)) 58 | data_secs = (now - int(now)) * 1000 59 | return int(date + str("%03d" % data_secs)) 60 | -------------------------------------------------------------------------------- /libs/scope.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 鉴权判断方法 5 | @Author: Zpp 6 | @Date: 2019-09-04 16:55:43 7 | LastEditTime: 2020-12-01 13:39:07 8 | LastEditors: Zpp 9 | ''' 10 | from models import db 11 | from models.system import Admin, Role, Interface, Menu, InitSql 12 | from sqlalchemy import exists 13 | import json 14 | 15 | 16 | def checkDb(): 17 | s = db.session() 18 | try: 19 | res = s.query(InitSql).first() 20 | return res.is_init 21 | except Exception as e: 22 | return str('数据库未连接或者其他错误请查看错误信息:%s' % e) 23 | 24 | 25 | def isExists(s, model, params): 26 | ''' 27 | 判断值是否已存在 28 | ''' 29 | try: 30 | for i in params: 31 | querys = s.query(model).filter_by(**{ 32 | i: params[i]['value'] 33 | }) 34 | 35 | is_exists = s.query( 36 | querys.exists() 37 | ).scalar() 38 | 39 | if is_exists: 40 | return { 41 | 'value': querys.one(), 42 | 'error': '%s已存在' % params[i]['name'] 43 | } 44 | 45 | return True 46 | except Exception as e: 47 | print(e) 48 | return True 49 | 50 | 51 | def is_in_scope(admin_id, path): 52 | ''' 53 | 路由鉴权判断 54 | ''' 55 | s = db.session() 56 | try: 57 | admin = s.query(Admin).filter(Admin.admin_id == admin_id).first() 58 | if not admin: 59 | return str('管理员不存在') 60 | if admin.disable: 61 | return str('管理员被禁用') 62 | 63 | role = s.query(Role).filter(Role.role_id == admin.role_id, Role.disable == False).one() 64 | if role: 65 | interface = role.interfaces.filter(Interface.disable == False, Interface.path == path) 66 | if interface: 67 | return True 68 | 69 | menu = role.menus.filter(Menu.disable == False, Menu.path == path) 70 | if menu: 71 | return True 72 | 73 | return False 74 | except Exception as e: 75 | print(e) 76 | return False 77 | -------------------------------------------------------------------------------- /libs/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 工具 5 | @Author: Zpp 6 | @Date: 2019-10-28 11:28:09 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-24 16:27:50 9 | ''' 10 | import platform 11 | 12 | 13 | def IsWindows(): 14 | return True if platform.system() == 'Windows' else False 15 | 16 | 17 | def ReadFile(path, type='r'): 18 | try: 19 | f = open(path, type) 20 | content = f.read() 21 | f.close() 22 | return content 23 | except: 24 | return False 25 | 26 | 27 | def WriteFile(path, content, type='w'): 28 | try: 29 | f = open(path, type) 30 | f.write(content) 31 | f.close() 32 | return True 33 | except: 34 | return False 35 | 36 | 37 | def health_database_status(s, sql): 38 | is_db = True 39 | 40 | try: 41 | s.execute(sql) 42 | except: 43 | is_db = False 44 | 45 | return is_db 46 | -------------------------------------------------------------------------------- /logs/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 日志初始化 5 | @Author: Zpp 6 | @Date: 2019-09-12 16:38:25 7 | @LastEditors: Zpp 8 | @LastEditTime: 2019-09-18 15:57:06 9 | ''' 10 | import logging 11 | from logging.handlers import TimedRotatingFileHandler 12 | from conf.setting import log_info 13 | 14 | 15 | def init_app(): 16 | formatter = logging.Formatter('%(asctime)s - %(module)s %(filename)s %(funcName)s:%(lineno)s - %(name)s -%(message)s') 17 | 18 | logging.root.setLevel(logging.INFO) 19 | 20 | console = logging.StreamHandler() 21 | console.setLevel(logging.INFO) 22 | console.setFormatter(logging.Formatter('%(message)s')) 23 | console.addFilter(logging.Filter()) 24 | logging.root.addHandler(console) 25 | 26 | file_handler_info = TimedRotatingFileHandler(filename=log_info['LOG_PATH_INFO'], backupCount=log_info['LOG_FILE_BACKUP_COUNT'], when='D', interval=1, encoding='utf-8') 27 | file_handler_info.setFormatter(formatter) 28 | file_handler_info.setLevel(logging.INFO) 29 | file_handler_info.addFilter(logging.Filter()) 30 | logging.root.addHandler(file_handler_info) 31 | 32 | return True 33 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-04 10:23:51 7 | LastEditTime: 2022-04-11 11:11:36 8 | LastEditors: Zpp 9 | ''' 10 | from conf.setting import _config 11 | from flask_sqlalchemy import SQLAlchemy 12 | import pymysql 13 | pymysql.install_as_MySQLdb() 14 | 15 | db = SQLAlchemy() 16 | 17 | 18 | def init_app(app): 19 | # mysql 数据库连接数据 20 | app.config['SQLALCHEMY_DATABASE_URI'] = _config.get_sql_url() 21 | # 禁用追踪对象的修改并且发送信号(会需要额外内存) 22 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 23 | # 关闭连接池 24 | app.config['SQLALCHEMY_POOL_SIZE'] = None 25 | 26 | global db 27 | db.init_app(app) 28 | -------------------------------------------------------------------------------- /models/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 日志表结构 5 | @Author: Zpp 6 | @Date: 2019-09-12 16:34:07 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-04-26 15:33:18 9 | ''' 10 | from models import db 11 | import datetime 12 | 13 | 14 | class Log(db.Model): 15 | ''' 16 | 日志 17 | ''' 18 | __tablename__ = 'db_log' 19 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 20 | username = db.Column(db.String(64), index=True, nullable=False) 21 | content = db.Column(db.Text) # 错误内容 22 | path = db.Column(db.Text, nullable=False) 23 | method = db.Column(db.String(36), nullable=False) 24 | params = db.Column(db.Text) # 请求参数 25 | ip = db.Column(db.String(255)) 26 | time = db.Column(db.Integer, nullable=False) 27 | status = db.Column(db.SmallInteger, nullable=False, index=True, default=1) # 0 成功 1 失败 2 禁用 28 | type = db.Column(db.SmallInteger, nullable=False, index=True, default=1) # 0 其他 1 登录 29 | create_time = db.Column(db.DateTime, index=True, default=datetime.datetime.now) 30 | __table_args__ = { 31 | 'useexisting': True, 32 | 'mysql_engine': 'InnoDB' 33 | } 34 | 35 | def to_json(self): 36 | dict = self.__dict__ 37 | if "_sa_instance_state" in dict: 38 | del dict["_sa_instance_state"] 39 | if "create_time" in dict: 40 | dict["create_time"] = dict["create_time"].strftime('%Y-%m-%d %H:%M:%S') 41 | return dict 42 | 43 | def __repr__(self): 44 | return '' % self.id 45 | -------------------------------------------------------------------------------- /models/system.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 系统相关的几张表结构 5 | @Author: Zpp 6 | @Date: 2019-09-05 15:57:55 7 | LastEditTime: 2020-11-26 16:00:20 8 | LastEditors: Zpp 9 | ''' 10 | from models import db 11 | import datetime 12 | import json 13 | 14 | 15 | def table_args(): 16 | return { 17 | 'useexisting': True, 18 | 'mysql_engine': 'InnoDB', 19 | 'mysql_charset': 'utf8' 20 | } 21 | 22 | 23 | InterfaceToMenu = db.Table( 24 | 'db_interface_to_menu', 25 | db.Column('menu_id', db.String(36), db.ForeignKey('db_menu.menu_id', ondelete='CASCADE')), 26 | db.Column('interface_id', db.String(36), db.ForeignKey('db_interface.interface_id', ondelete='CASCADE')), 27 | useexisting=True, 28 | mysql_engine='InnoDB', 29 | mysql_charset='utf8' 30 | ) 31 | 32 | InterfaceToRole = db.Table( 33 | 'db_interface_to_role', 34 | db.Column('role_id', db.String(36), db.ForeignKey('db_role.role_id', ondelete='CASCADE')), 35 | db.Column('interface_id', db.String(36), db.ForeignKey('db_interface.interface_id', ondelete='CASCADE')), 36 | useexisting=True, 37 | mysql_engine='InnoDB', 38 | mysql_charset='utf8' 39 | ) 40 | 41 | MenuToRole = db.Table( 42 | 'db_menu_to_role', 43 | db.Column('role_id', db.String(36), db.ForeignKey('db_role.role_id', ondelete='CASCADE')), 44 | db.Column('menu_id', db.String(36), db.ForeignKey('db_menu.menu_id', ondelete='CASCADE')), 45 | useexisting=True, 46 | mysql_engine='InnoDB', 47 | mysql_charset='utf8' 48 | ) 49 | 50 | 51 | class Admin(db.Model): 52 | ''' 53 | 管理员 54 | ''' 55 | __tablename__ = 'db_admin' 56 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 57 | admin_id = db.Column(db.String(36), index=True, nullable=False, unique=True) 58 | username = db.Column(db.String(64), index=True, nullable=False, unique=True) 59 | password = db.Column(db.String(32), nullable=False) 60 | nickname = db.Column(db.String(64), default=None) 61 | email = db.Column(db.String(255), default=None) 62 | sex = db.Column(db.SmallInteger, default=1) 63 | avatar = db.Column(db.String(255), default=None) 64 | disable = db.Column(db.Boolean, index=True, default=False) 65 | create_time = db.Column(db.DateTime, index=True, default=datetime.datetime.now) 66 | update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now) 67 | role_id = db.Column(db.String(36), db.ForeignKey('db_role.role_id', ondelete='CASCADE')) 68 | __table_args__ = (table_args()) 69 | 70 | def is_authenticated(self): 71 | return True 72 | 73 | def is_active(self): 74 | return True 75 | 76 | def is_anonymous(self): 77 | return False 78 | 79 | def get_id(self): 80 | return str(self.admin_id) 81 | 82 | def to_json(self): 83 | dict = self.__dict__ 84 | if "_sa_instance_state" in dict: 85 | del dict["_sa_instance_state"] 86 | if "update_time" in dict: 87 | dict["update_time"] = dict["update_time"].strftime('%Y-%m-%d %H:%M:%S') 88 | if "create_time" in dict: 89 | dict["create_time"] = dict["create_time"].strftime('%Y-%m-%d %H:%M:%S') 90 | return dict 91 | 92 | def __repr__(self): 93 | return '' % self.username 94 | 95 | 96 | class LoginLock(db.Model): 97 | ''' 98 | 登录锁定 99 | ''' 100 | __tablename__ = 'db_login_lock' 101 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 102 | lock_id = db.Column(db.String(36), index=True, nullable=False, unique=True) 103 | user_id = db.Column(db.String(36), index=True, nullable=False) 104 | flag = db.Column(db.Boolean, index=True, default=False) # 是否锁定 105 | number = db.Column(db.Integer, primary_key=True, default=0) 106 | ip = db.Column(db.String(36), index=True) 107 | lock_time = db.Column(db.DateTime) 108 | __table_args__ = (table_args()) 109 | 110 | def to_json(self): 111 | dict = self.__dict__ 112 | if "_sa_instance_state" in dict: 113 | del dict["_sa_instance_state"] 114 | if "lock_time" in dict: 115 | dict["lock_time"] = dict["lock_time"].strftime('%Y-%m-%d %H:%M:%S') 116 | return dict 117 | 118 | def __repr__(self): 119 | return '' % self.lock_id 120 | 121 | 122 | class Role(db.Model): 123 | ''' 124 | 鉴权 125 | ''' 126 | __tablename__ = 'db_role' 127 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 128 | role_id = db.Column(db.String(36), index=True, nullable=False, unique=True) 129 | name = db.Column(db.String(64), nullable=False, unique=True) 130 | mark = db.Column(db.String(64), nullable=False, unique=True) 131 | disable = db.Column(db.Boolean, index=True, default=False) 132 | admins = db.relationship('Admin', backref='role') 133 | interfaces = db.relationship('Interface', 134 | secondary=InterfaceToRole, 135 | backref=db.backref('roles', lazy='dynamic'), 136 | lazy='dynamic') 137 | menus = db.relationship('Menu', 138 | secondary=MenuToRole, 139 | backref=db.backref('roles', lazy='dynamic'), 140 | lazy='dynamic') 141 | __table_args__ = (table_args()) 142 | 143 | def to_json(self): 144 | dict = self.__dict__ 145 | if "_sa_instance_state" in dict: 146 | del dict["_sa_instance_state"] 147 | if "role_list" in dict: 148 | del dict["role_list"] 149 | return dict 150 | 151 | def __repr__(self): 152 | return '' % self.name 153 | 154 | 155 | class Menu(db.Model): 156 | ''' 157 | 菜单 158 | ''' 159 | __tablename__ = 'db_menu' 160 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 161 | menu_id = db.Column(db.String(36), index=True, nullable=False, unique=True) 162 | pid = db.Column(db.String(36), nullable=False, index=True, default='0') 163 | name = db.Column(db.String(64), index=True, nullable=False, unique=True) 164 | title = db.Column(db.String(64), nullable=False, unique=True) 165 | path = db.Column(db.String(255), nullable=False, unique=True) 166 | icon = db.Column(db.String(255), nullable=False) 167 | mark = db.Column(db.String(255), nullable=False, unique=True) 168 | component = db.Column(db.String(255), nullable=False) 169 | componentPath = db.Column(db.String(255), nullable=False) 170 | cache = db.Column(db.Boolean, index=True, default=True) 171 | sort = db.Column(db.SmallInteger, index=True, default=1) 172 | disable = db.Column(db.Boolean, index=True, default=False) 173 | interfaces = db.relationship('Interface', 174 | secondary=InterfaceToMenu, 175 | backref=db.backref('menus', lazy='dynamic'), 176 | lazy='dynamic') 177 | __table_args__ = (table_args()) 178 | 179 | def to_json(self): 180 | dict = self.__dict__ 181 | if "_sa_instance_state" in dict: 182 | del dict["_sa_instance_state"] 183 | return dict 184 | 185 | def __repr__(self): 186 | return '' % self.title 187 | 188 | 189 | class Interface(db.Model): 190 | ''' 191 | 接口 192 | ''' 193 | __tablename__ = 'db_interface' 194 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 195 | interface_id = db.Column(db.String(36), index=True, nullable=False, unique=True) 196 | name = db.Column(db.String(64), index=True, nullable=False, unique=True) 197 | path = db.Column(db.String(255), nullable=False) 198 | method = db.Column(db.String(36), nullable=False) 199 | description = db.Column(db.String(255), nullable=False) 200 | mark = db.Column(db.String(255), nullable=False, unique=True) 201 | disable = db.Column(db.Boolean, index=True, default=False) 202 | forbid = db.Column(db.Boolean, index=True, default=True) 203 | menu = db.relationship('Menu', 204 | secondary=InterfaceToMenu, 205 | backref=db.backref('interface', lazy='dynamic'), 206 | lazy='dynamic') 207 | role = db.relationship('Role', 208 | secondary=InterfaceToRole, 209 | backref=db.backref('interface', lazy='dynamic'), 210 | lazy='dynamic') 211 | __table_args__ = (table_args()) 212 | 213 | def to_json(self): 214 | menus = [] 215 | for i in self.menus.all(): 216 | menus.append(i.menu_id) 217 | 218 | dict = self.__dict__ 219 | if "_sa_instance_state" in dict: 220 | del dict["_sa_instance_state"] 221 | dict['menus'] = menus 222 | return dict 223 | 224 | def __repr__(self): 225 | return '' % self.name 226 | 227 | 228 | class Document(db.Model): 229 | ''' 230 | 附件 231 | ''' 232 | __tablename__ = 'db_document' 233 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 234 | document_id = db.Column(db.String(36), index=True, nullable=False, unique=True) 235 | admin_id = db.Column(db.String(36), index=True, nullable=False) 236 | name = db.Column(db.String(64), index=True, nullable=False) 237 | path = db.Column(db.String(255), nullable=False) 238 | status = db.Column(db.SmallInteger, index=True, default=1) # 1=图片 2=附件 3=头像 239 | ext = db.Column(db.String(64), nullable=False) 240 | size = db.Column(db.Integer, nullable=False) 241 | deleted = db.Column(db.Boolean, index=True, default=False) # True = 回收站 242 | create_time = db.Column(db.DateTime, index=True, default=datetime.datetime.now) 243 | folder_id = db.Column(db.String(36), db.ForeignKey('db_folder.folder_id', ondelete='CASCADE')) 244 | __table_args__ = (table_args()) 245 | 246 | def to_json(self): 247 | dict = self.__dict__ 248 | if "_sa_instance_state" in dict: 249 | del dict["_sa_instance_state"] 250 | if "create_time" in dict: 251 | dict["create_time"] = dict["create_time"].strftime('%Y-%m-%d %H:%M:%S') 252 | return dict 253 | 254 | def __repr__(self): 255 | return '' % self.name 256 | 257 | 258 | class Folder(db.Model): 259 | ''' 260 | 文件夹 261 | ''' 262 | __tablename__ = 'db_folder' 263 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 264 | folder_id = db.Column(db.String(36), index=True, nullable=False, unique=True) 265 | admin_id = db.Column(db.String(36), index=True, default=None) 266 | pid = db.Column(db.String(36), nullable=False, index=True, default='0') 267 | name = db.Column(db.String(36), index=True, nullable=False) 268 | is_sys = db.Column(db.Boolean, index=True, default=True) # True = 系统文件夹 269 | create_time = db.Column(db.DateTime, index=True, default=datetime.datetime.now) 270 | documents = db.relationship('Document', backref='folder') 271 | __table_args__ = (table_args()) 272 | 273 | def to_json(self): 274 | dict = self.__dict__ 275 | if "_sa_instance_state" in dict: 276 | del dict["_sa_instance_state"] 277 | if "create_time" in dict: 278 | dict["create_time"] = dict["create_time"].strftime('%Y-%m-%d %H:%M:%S') 279 | return dict 280 | 281 | def __repr__(self): 282 | return '' % self.name 283 | 284 | 285 | class InitSql(db.Model): 286 | ''' 287 | 是否已经初始化数据库 288 | ''' 289 | __tablename__ = 'db_init_sql' 290 | id = db.Column(db.Integer, nullable=False, primary_key=True, index=True, autoincrement=True) 291 | is_init = db.Column(db.Boolean, index=True, default=True) 292 | __table_args__ = (table_args()) 293 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.12.2 2 | Werkzeug==0.12.2 3 | Flask-Session 4 | aliyun-python-sdk-core 5 | Flask-Cache 6 | flask-sqlalchemy 7 | SQLAlchemy 8 | Pillow 9 | Flask-HTTPAuth 10 | Flask-Compress 11 | geoip2 12 | flask_socketio 13 | healthcheck 14 | PyMySQL 15 | gevent 16 | gevent-websocket -------------------------------------------------------------------------------- /routes/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: API蓝图初始化注册 5 | @Author: Zpp 6 | @Date: 2019-09-04 10:23:46 7 | LastEditTime: 2022-04-29 10:02:58 8 | LastEditors: Zpp 9 | ''' 10 | from .v1.admin import route_admin 11 | from .v1.menu import route_menu 12 | from .v1.role import route_role 13 | from .v1.interface import route_interface 14 | from .v1.document import route_document 15 | from .v1.folder import route_folder 16 | from .v1.log import route_log 17 | from .v1.base import route_base 18 | 19 | from flask import current_app, session, make_response, request 20 | from libs.code import ResultDeal, GetTimestamp 21 | from models import db 22 | from models.system import InitSql 23 | from datetime import datetime 24 | from libs.utils import health_database_status 25 | from healthcheck import HealthCheck 26 | 27 | 28 | def init_app(app): 29 | health = HealthCheck(app, "/healthcheck") 30 | 31 | @app.before_first_request 32 | def before_first_request(): 33 | session['requestTime'] = GetTimestamp() 34 | 35 | # 运行检查是否连接数据库 36 | s = db.session() 37 | if health.add_check(health_database_status(s, 'SELECT 1')): 38 | # 检查是否存在数据表 39 | if health.add_check(health_database_status(s, 'SELECT * FROM db_init_sql')): 40 | res = s.query(InitSql).first() 41 | if not res: 42 | s.add(InitSql(is_init=False)) 43 | s.commit() 44 | else: 45 | # 创建数据表 46 | db.create_all() 47 | else: 48 | return ResultDeal(code=-1, msg='数据库未连接或者连接配置错误') 49 | 50 | @app.before_request 51 | def handel_before_request(): 52 | session['requestTime'] = GetTimestamp() 53 | 54 | @app.teardown_request 55 | def handel_teardown_request(response): 56 | if response: 57 | db.session.close() 58 | 59 | @app.errorhandler(401) 60 | def handle_401_error(error): 61 | return '登录认证失效,请重新登录', 401 62 | 63 | @app.errorhandler(403) 64 | def handle_403_error(error): 65 | return '您没有访问权限', 403 66 | 67 | @app.errorhandler(404) 68 | def handle_404_error(error): 69 | return '文件或者页面不存在', 404 70 | 71 | @app.errorhandler(405) 72 | def handle_404_error(error): 73 | return '方法不被允许', 405 74 | 75 | @app.errorhandler(500) 76 | def handle_500_error(error): 77 | return '服务器错误 % s' % error, 500 78 | 79 | @app.route('/favicon.ico') 80 | def favicon(): 81 | return current_app.send_static_file('favicon.ico') 82 | 83 | routes = [ 84 | route_admin, 85 | route_menu, 86 | route_role, 87 | route_interface, 88 | route_document, 89 | route_folder, 90 | route_log, 91 | route_base 92 | ] 93 | 94 | for r in routes: 95 | app.register_blueprint(r) 96 | -------------------------------------------------------------------------------- /routes/token_auth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-04 16:06:14 7 | LastEditTime: 2022-04-29 10:18:07 8 | LastEditors: Zpp 9 | ''' 10 | 11 | from flask import current_app, request, session, abort 12 | from flask_httpauth import HTTPBasicAuth 13 | from functools import wraps 14 | from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired 15 | from libs.scope import is_in_scope 16 | 17 | auth = HTTPBasicAuth() 18 | 19 | 20 | @auth.verify_password 21 | def verify_password(token, pwd): 22 | # token 23 | # HTTP 账号密码 24 | # header key:value 25 | # account 账号 26 | # 密码 27 | # key=随机 28 | # value =basic base64(账号:密码) 29 | if not token: 30 | return False 31 | 32 | admin_info = verify_auth_token(token, pwd) 33 | if not admin_info: 34 | return False 35 | else: 36 | return True 37 | 38 | 39 | @auth.error_handler 40 | def unauthorized(): 41 | abort(401) 42 | 43 | 44 | def generate_auth_token(params, expiration=24 * 3600): 45 | """ 46 | 生成token 47 | params 参数 48 | """ 49 | s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration) 50 | return s.dumps(params).decode() 51 | 52 | 53 | def verify_auth_token(token, pwd): 54 | info = session.get('admin') 55 | if not info or info != token: 56 | return False 57 | 58 | data = get_auth_token(token) 59 | return data if data.get('password') == pwd else False 60 | 61 | 62 | def get_auth_token(token): 63 | s = Serializer(current_app.config['SECRET_KEY']) 64 | try: 65 | data = s.loads(token) 66 | except SignatureExpired: 67 | return False 68 | except BadSignature: 69 | return False 70 | 71 | return data 72 | 73 | 74 | def validate_current_access(f): 75 | @wraps(f) 76 | def decorated_function(*args, **kws): 77 | # 路由鉴权 78 | info = get_auth_token(session.get('admin')) 79 | if info['is_admin']: 80 | return f(*args, **kws) 81 | 82 | allow = is_in_scope(info['admin_id'], request.path) 83 | if not allow: 84 | abort(403) 85 | 86 | return f(*args, **kws) 87 | 88 | return decorated_function 89 | -------------------------------------------------------------------------------- /routes/v1/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-09 10:07:23 7 | @LastEditTime: 2019-09-12 11:28:24 8 | @LastEditors: Zpp 9 | ''' 10 | -------------------------------------------------------------------------------- /routes/v1/admin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 管理员API 5 | @Author: Zpp 6 | @Date: 2019-09-06 14:19:29 7 | @LastEditTime: 2020-06-05 10:24:47 8 | @LastEditors: Zpp 9 | ''' 10 | from flask import Blueprint, request, make_response, session 11 | from collection.v1.admin import AdminModel 12 | from ..token_auth import auth, generate_auth_token, validate_current_access, get_auth_token 13 | from conf.setting import default 14 | from libs.code import ResultDeal 15 | from libs.captcha import Captcha 16 | from io import BytesIO 17 | from libs.scope import checkDb 18 | from validate import validate_form 19 | from validate.v1.admin import params 20 | import json 21 | 22 | route_admin = Blueprint('Admin', __name__, url_prefix='/v1/Admin') 23 | validate = validate_form(params) 24 | 25 | 26 | @route_admin.route('/checkDb', methods=['GET']) 27 | def CheckDb(): 28 | result = checkDb() 29 | if type(result).__name__ == 'str': 30 | return ResultDeal(msg=result, code=-1) 31 | 32 | return ResultDeal(data=result) 33 | 34 | 35 | @route_admin.route('/Captcha', methods=['GET']) 36 | def GetCaptcha(): 37 | text, image = Captcha().gen_graph_captcha() 38 | out = BytesIO() 39 | image.save(out, 'png') 40 | out.seek(0) 41 | resp = make_response(out.read()) 42 | resp.content_type = 'image/png' 43 | # 存入session 44 | session['Captcha'] = text 45 | return resp 46 | 47 | 48 | @route_admin.route('/Login', methods=['POST'], endpoint='Login') 49 | @validate.form('Login') 50 | def Login(): 51 | # 验证码校验 52 | captcha = request.form.get('code') 53 | sesson_captcha = session.get('Captcha') 54 | 55 | if not sesson_captcha: 56 | return ResultDeal(msg=str('验证码已过期, 请刷新'), code=-1) 57 | 58 | if session.get('Captcha').lower() != captcha.lower(): 59 | return ResultDeal(msg=str('验证码不正确'), code=-1) 60 | 61 | result = AdminModel().GetAdminRequest( 62 | username=request.form.get('username'), 63 | password=request.form.get('password') 64 | ) 65 | 66 | if type(result).__name__ == 'str': 67 | return ResultDeal(msg=result, code=-1) 68 | 69 | try: 70 | user = result['user'] 71 | user['is_admin'] = user['mark'] == default['role_mark'] 72 | 73 | token = generate_auth_token({ 74 | 'admin_id': user['admin_id'], 75 | 'password': user['password'], 76 | 'is_admin': user['mark'] == default['role_mark'] 77 | }) 78 | 79 | session['admin'] = token 80 | session['username'] = user['username'] 81 | 82 | return ResultDeal(data={ 83 | 'token': token, 84 | 'menus': result['menus'], 85 | 'interface': result['interface'], 86 | 'info': user 87 | }) 88 | except Exception as e: 89 | print(e) 90 | return ResultDeal(msg=e, code=-1) 91 | 92 | 93 | @route_admin.route('/Logout', methods=['GET']) 94 | def Logout(): 95 | session.pop('admin') 96 | return ResultDeal() 97 | 98 | 99 | @route_admin.route('/CreateAdmin', methods=['POST'], endpoint='CreateAdmin') 100 | @auth.login_required 101 | @validate_current_access 102 | @validate.form('Create') 103 | def CreateAdmin(): 104 | result = AdminModel().CreateAdminRequest(request.form) 105 | 106 | if type(result).__name__ == 'str': 107 | return ResultDeal(msg=result, code=-1) 108 | 109 | return ResultDeal(data=result) 110 | 111 | 112 | @route_admin.route('/LockAdmin', methods=['POST'], endpoint='LockAdmin') 113 | @auth.login_required 114 | @validate_current_access 115 | @validate.form('Lock') 116 | def LockAdmin(): 117 | result = AdminModel().LockAdminRequest( 118 | admin_id=request.form.getlist('admin_id[]'), 119 | disable=request.form.get('disable') 120 | ) 121 | 122 | if type(result).__name__ == 'str': 123 | return ResultDeal(msg=result, code=-1) 124 | 125 | return ResultDeal(data=result) 126 | 127 | 128 | @route_admin.route('/DelAdmin', methods=['POST'], endpoint='DelAdmin') 129 | @auth.login_required 130 | @validate_current_access 131 | @validate.form('Del') 132 | def DelAdmin(): 133 | result = AdminModel().DelAdminRequest(request.form.getlist('admin_id[]')) 134 | 135 | if type(result).__name__ == 'str': 136 | return ResultDeal(msg=result, code=-1) 137 | 138 | return ResultDeal(data=result) 139 | 140 | 141 | @route_admin.route('/ModifyAdmin', methods=['POST'], endpoint='ModifyAdmin') 142 | @auth.login_required 143 | @validate_current_access 144 | @validate.form('Modify') 145 | def ModifyAdmin(): 146 | result = AdminModel().ModifyAdminRequest( 147 | admin_id=request.form.get('admin_id'), 148 | params=request.form 149 | ) 150 | 151 | if type(result).__name__ == 'str': 152 | return ResultDeal(msg=result, code=-1) 153 | 154 | token = session.get('admin') 155 | info = get_auth_token(token) 156 | if info['admin_id'] == request.form.get('admin_id'): 157 | token = generate_auth_token({ 158 | 'admin_id': result['admin_id'], 159 | 'password': result['password'], 160 | 'is_admin': True if result['mark'] == default['role_mark'] else False 161 | }) 162 | 163 | session['admin'] = token 164 | session['username'] = result['username'] 165 | 166 | return ResultDeal(data={ 167 | 'user': result, 168 | 'token': token, 169 | 'is_self': True 170 | }) 171 | 172 | return ResultDeal(data={ 173 | 'user': result, 174 | 'token': None, 175 | 'is_self': False 176 | }) 177 | 178 | 179 | @route_admin.route('/QueryAdminByParam', methods=['POST'], endpoint='QueryAdminByParam') 180 | @auth.login_required 181 | @validate_current_access 182 | @validate.form('Query') 183 | def QueryAdminByParam(): 184 | params = {} 185 | if request.form.get('disable') != None: 186 | params['disable'] = request.form.get('disable') 187 | if request.form.get('role_id'): 188 | params['role_id'] = request.form.get('role_id') 189 | 190 | result = AdminModel().QueryAdminByParamRequest( 191 | params=params, 192 | page=int(request.form.get('page')), 193 | page_size=int(request.form.get('page_size')), 194 | order_by=request.form.get('order_by') 195 | ) 196 | 197 | if type(result).__name__ == 'str': 198 | return ResultDeal(msg=result, code=-1) 199 | 200 | return ResultDeal(data=result) 201 | -------------------------------------------------------------------------------- /routes/v1/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 数据库API 5 | @Author: Zpp 6 | @Date: 2020-02-21 13:02:28 7 | @LastEditTime: 2020-06-08 10:00:40 8 | @LastEditors: Zpp 9 | ''' 10 | from flask import Blueprint, request, make_response, session, abort 11 | from collection.v1.base import BaseModel 12 | from ..token_auth import auth, generate_auth_token, validate_current_access, get_auth_token 13 | from libs.code import ResultDeal 14 | from libs.utils import ReadFile 15 | from validate import validate_form 16 | from validate.v1.base import params 17 | import json 18 | import os 19 | 20 | route_base = Blueprint('Base', __name__, url_prefix='/v1/Base') 21 | validate = validate_form(params) 22 | 23 | 24 | @route_base.route('/CreateDrop', methods=['GET']) 25 | def CreateDrop(): 26 | result = BaseModel().CreateDropRequest(False) 27 | if type(result).__name__ == 'str': 28 | return ResultDeal(msg=result, code=-1) 29 | 30 | return ResultDeal(data=result) 31 | 32 | 33 | @route_base.route('/AgainCreateDrop', methods=['GET']) 34 | @auth.login_required 35 | @validate_current_access 36 | def AgainCreateDrop(): 37 | result = BaseModel().CreateDropRequest(True, get_auth_token(session.get('admin'))) 38 | if type(result).__name__ == 'str': 39 | return ResultDeal(msg=result, code=-1) 40 | 41 | return ResultDeal(data=result) 42 | 43 | 44 | @route_base.route('/ExportSql', methods=['POST'], endpoint='ExportSql') 45 | @auth.login_required 46 | @validate_current_access 47 | @validate.form('Export') 48 | def ExportSql(): 49 | result = BaseModel().ExportSql(int(request.form.get('type'))) 50 | 51 | if type(result).__name__ == 'str': 52 | return ResultDeal(msg=result, code=-1) 53 | 54 | if os.path.exists(result['path']): 55 | res = make_response(ReadFile(result['path'], 'rb')) 56 | res.headers['Content-Type'] = 'application/octet-stream' 57 | res.headers['filename'] = result['name'] 58 | res.headers['Content-Disposition'] = 'attachment; filename=' + result['name'] 59 | return res 60 | else: 61 | abort(404) 62 | 63 | 64 | @route_base.route('/ImportSql', methods=['POST'], endpoint='ImportSql') 65 | @auth.login_required 66 | @validate_current_access 67 | @validate.form('Import') 68 | def ImportSql(): 69 | result = BaseModel().ImportSql(request.files.get('document')) 70 | 71 | if type(result).__name__ == 'str': 72 | return ResultDeal(msg=result, code=-1) 73 | 74 | return ResultDeal(data=result) 75 | 76 | 77 | @route_base.route('/GetLoginInfo', methods=['POST'], endpoint='GetLoginInfo') 78 | @auth.login_required 79 | @validate_current_access 80 | @validate.form('Login') 81 | def GetLoginInfo(): 82 | result = BaseModel().GetLoginInfo( 83 | request.form.get('admin_id'), 84 | request.form.get('time') 85 | ) 86 | 87 | if type(result).__name__ == 'str': 88 | return ResultDeal(msg=result, code=-1) 89 | 90 | return ResultDeal(data=result) 91 | 92 | 93 | @route_base.route('/GetAllUserLoginCount', methods=['POST']) 94 | @auth.login_required 95 | @validate_current_access 96 | def GetAllUserLoginCount(): 97 | result = BaseModel().GetAllUserLoginCount() 98 | 99 | if type(result).__name__ == 'str': 100 | return ResultDeal(msg=result, code=-1) 101 | 102 | return ResultDeal(data=result) 103 | 104 | 105 | @route_base.route('/GetUserLoginIp', methods=['POST']) 106 | @auth.login_required 107 | @validate_current_access 108 | def GetUserLoginIp(): 109 | result = BaseModel().GetUserLoginIp() 110 | 111 | if type(result).__name__ == 'str': 112 | return ResultDeal(msg=result, code=-1) 113 | 114 | return ResultDeal(data=result) 115 | 116 | 117 | @route_base.route('/GetReadmeContent', methods=['POST']) 118 | @auth.login_required 119 | @validate_current_access 120 | def GetReadmeContent(): 121 | result = BaseModel().GetReadmeContent() 122 | 123 | if type(result).__name__ == 'str': 124 | return ResultDeal(msg=result, code=-1) 125 | 126 | return ResultDeal(data=result) 127 | -------------------------------------------------------------------------------- /routes/v1/document.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 附件API 5 | @Author: Zpp 6 | @Date: 2019-10-14 15:56:20 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-28 15:02:54 9 | ''' 10 | from flask import Blueprint, request, make_response, abort, send_from_directory, session 11 | from collection.v1.document import DocumentModel 12 | from ..token_auth import auth, validate_current_access, get_auth_token 13 | from libs.code import ResultDeal 14 | from conf.setting import document_dir 15 | from libs.utils import ReadFile 16 | from validate import validate_form 17 | from validate.v1.document import params 18 | import os 19 | import base64 20 | import mimetypes 21 | 22 | route_document = Blueprint('Document', __name__, url_prefix='/v1/Document') 23 | validate = validate_form(params) 24 | 25 | 26 | @route_document.route('/CreateDocument', methods=['POST'], endpoint='CreateDocument') 27 | @auth.login_required 28 | @validate_current_access 29 | @validate.form('Create') 30 | def CreateDocument(): 31 | result = DocumentModel().CreateDocumentRequest( 32 | request.files.getlist('document'), 33 | request.form 34 | ) 35 | return ResultDeal(data=result) 36 | 37 | 38 | @route_document.route('/GetDocument/', methods=['GET']) 39 | def GetDocument(filename): 40 | path = os.path.join(document_dir, filename) 41 | if os.path.exists(path): 42 | return send_from_directory(document_dir, filename) 43 | else: 44 | abort(404) 45 | 46 | 47 | @route_document.route('/DownDocument//', methods=['GET']) 48 | @auth.login_required 49 | @validate_current_access 50 | def DownDocument(filename, name): 51 | path = os.path.join(document_dir, filename) 52 | if os.path.exists(path): 53 | res = make_response(ReadFile(path, 'rb')) 54 | res.headers['Content-Type'] = 'application/octet-stream' 55 | res.headers['Content-Disposition'] = 'attachment; filename=' + name 56 | return res 57 | else: 58 | abort(404) 59 | 60 | 61 | @route_document.route('/RetrieveDocument', methods=['POST'], endpoint='RetrieveDocument') 62 | @auth.login_required 63 | @validate_current_access 64 | @validate.form('Retrieve') 65 | def RetrieveDocument(): 66 | result = DocumentModel().RetrieveDocument( 67 | request.form.getlist('document_id[]'), 68 | request.form.get('deleted') 69 | ) 70 | 71 | if type(result).__name__ == 'str': 72 | return ResultDeal(msg=result, code=-1) 73 | 74 | return ResultDeal(data=result) 75 | 76 | 77 | @route_document.route('/DelDocument', methods=['POST'], endpoint='DelDocument') 78 | @auth.login_required 79 | @validate_current_access 80 | @validate.form('Del') 81 | def DelDocument(): 82 | result = DocumentModel().DelDocument(request.form.getlist('document_id[]')) 83 | 84 | if type(result).__name__ == 'str': 85 | return ResultDeal(msg=result, code=-1) 86 | 87 | return ResultDeal(data=result) 88 | 89 | 90 | @route_document.route('/QueryDocumentByParam', methods=['POST'], endpoint='QueryDocumentByParam') 91 | @auth.login_required 92 | @validate_current_access 93 | @validate.form('Query') 94 | def QueryDocumentByParam(): 95 | params = {} 96 | Ary = ['status', 'deleted', 'folder_id'] 97 | for i in Ary: 98 | if request.form.get(i) != None: 99 | params[i] = request.form.get(i) 100 | 101 | result = DocumentModel().QueryDocumentByParamRequest( 102 | params=params, 103 | page=int(request.form.get('page')), 104 | page_size=int(request.form.get('page_size')), 105 | order_by=request.form.get('order_by') 106 | ) 107 | 108 | if type(result).__name__ == 'str': 109 | return ResultDeal(msg=result, code=-1) 110 | 111 | return ResultDeal(data=result) 112 | -------------------------------------------------------------------------------- /routes/v1/folder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 文件夹API 5 | @Author: Zpp 6 | @Date: 2019-12-23 15:42:19 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-06-05 09:53:03 9 | ''' 10 | from flask import Blueprint, request, session 11 | from collection.v1.folder import FolderModel 12 | from ..token_auth import auth, validate_current_access, get_auth_token 13 | from libs.code import ResultDeal 14 | from validate import validate_form 15 | from validate.v1.folder import params 16 | 17 | route_folder = Blueprint('Folder', __name__, url_prefix='/v1/Folder') 18 | validate = validate_form(params) 19 | 20 | 21 | @route_folder.route('/CreateFolder', methods=['POST'], endpoint='CreateFolder') 22 | @auth.login_required 23 | @validate_current_access 24 | @validate.form('Create') 25 | def CreateFolder(): 26 | result = FolderModel().CreateFolderRequest(request.form) 27 | 28 | if type(result).__name__ == 'str': 29 | return ResultDeal(msg=result, code=-1) 30 | 31 | return ResultDeal(data=result) 32 | 33 | 34 | @route_folder.route('/DelFolder', methods=['POST'], endpoint='DelFolder') 35 | @auth.login_required 36 | @validate_current_access 37 | @validate.form('Del') 38 | def DelFolder(): 39 | result = FolderModel().DelFolderRequest(request.form.get('folder_id')) 40 | 41 | if type(result).__name__ == 'str': 42 | return ResultDeal(msg=result, code=-1) 43 | 44 | return ResultDeal(data=result) 45 | 46 | 47 | @route_folder.route('/ModifyFolder', methods=['POST'], endpoint='ModifyFolder') 48 | @auth.login_required 49 | @validate_current_access 50 | @validate.form('Modify') 51 | def ModifyFolder(): 52 | params = {'name': request.form.get('name')} 53 | if request.form.get('pid') != None: 54 | params['pid'] = request.form.get('pid') 55 | 56 | result = FolderModel().ModifyFolderRequest(request.form.get('folder_id'), params=params) 57 | 58 | if type(result).__name__ == 'str': 59 | return ResultDeal(msg=result, code=-1) 60 | 61 | return ResultDeal(data=result) 62 | 63 | 64 | @route_folder.route('/QueryFolderByParam', methods=['POST'], endpoint='QueryFolderByParam') 65 | @auth.login_required 66 | @validate_current_access 67 | @validate.form('Query') 68 | def QueryFolderByParam(): 69 | result = FolderModel().QueryFolderByParamRequest( 70 | pid=request.form.get('pid'), 71 | admin_id=request.form.get('admin_id') 72 | ) 73 | 74 | if type(result).__name__ == 'str': 75 | return ResultDeal(msg=result, code=-1) 76 | 77 | return ResultDeal(data=result) 78 | -------------------------------------------------------------------------------- /routes/v1/interface.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 接口API 5 | @Author: Zpp 6 | @Date: 2019-10-14 13:50:25 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-26 14:10:54 9 | ''' 10 | from flask import Blueprint, request 11 | from collection.v1.interface import InterfaceModel 12 | from ..token_auth import auth, validate_current_access 13 | from libs.code import ResultDeal 14 | from validate import validate_form 15 | from validate.v1.interface import params 16 | import json 17 | 18 | route_interface = Blueprint('Interface', __name__, url_prefix='/v1/Interface') 19 | validate = validate_form(params) 20 | 21 | 22 | @route_interface.route('/CreateInterface', methods=['POST'], endpoint='CreateInterface') 23 | @auth.login_required 24 | @validate_current_access 25 | @validate.form('Create') 26 | def CreateInterface(): 27 | result = InterfaceModel().CreateInterfaceRequest(request.form) 28 | 29 | if type(result).__name__ == 'str': 30 | return ResultDeal(msg=result, code=-1) 31 | 32 | return ResultDeal(data=result) 33 | 34 | 35 | @route_interface.route('/LockInterface', methods=['POST'], endpoint='LockInterface') 36 | @auth.login_required 37 | @validate_current_access 38 | @validate.form('Lock') 39 | def LockInterface(): 40 | result = InterfaceModel().LockInterfaceRequest( 41 | request.form.getlist('interface_id[]'), 42 | request.form.get('disable') 43 | ) 44 | 45 | if type(result).__name__ == 'str': 46 | return ResultDeal(msg=result, code=-1) 47 | 48 | return ResultDeal(data=result) 49 | 50 | 51 | @route_interface.route('/DelInterface', methods=['POST'], endpoint='DelInterface') 52 | @auth.login_required 53 | @validate_current_access 54 | @validate.form('Del') 55 | def DelInterface(): 56 | result = InterfaceModel().DelInterfaceRequest(request.form.getlist('interface_id[]')) 57 | 58 | if type(result).__name__ == 'str': 59 | return ResultDeal(msg=result, code=-1) 60 | 61 | return ResultDeal(data=result) 62 | 63 | 64 | @route_interface.route('/ModifyInterface', methods=['POST'], endpoint='ModifyInterface') 65 | @auth.login_required 66 | @validate_current_access 67 | @validate.form('Modify') 68 | def ModifyInterface(): 69 | result = InterfaceModel().ModifyInterfaceRequest(request.form.get('interface_id'), request.form) 70 | 71 | if type(result).__name__ == 'str': 72 | return ResultDeal(msg=result, code=-1) 73 | 74 | return ResultDeal(data=result) 75 | 76 | 77 | @route_interface.route('/QueryInterfaceByParam', methods=['POST'], endpoint='QueryInterfaceByParam') 78 | @auth.login_required 79 | @validate_current_access 80 | @validate.form('Query') 81 | def QueryInterfaceByParam(): 82 | result = InterfaceModel().QueryInterfaceByParamRequest( 83 | params=request.form, 84 | page=int(request.form.get('page')), 85 | page_size=int(request.form.get('page_size')), 86 | order_by=request.form.get('order_by') 87 | ) 88 | 89 | if type(result).__name__ == 'str': 90 | return ResultDeal(msg=result, code=-1) 91 | 92 | return ResultDeal(data=result) 93 | -------------------------------------------------------------------------------- /routes/v1/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 日志API 5 | @Author: Zpp 6 | @Date: 2019-10-17 15:46:30 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-29 14:06:22 9 | ''' 10 | from flask import Blueprint, request 11 | from collection.v1.log import LogModel 12 | from ..token_auth import auth, validate_current_access 13 | from libs.code import ResultDeal 14 | from validate import validate_form 15 | from validate.v1.log import params 16 | 17 | route_log = Blueprint('Log', __name__, url_prefix='/v1/Log') 18 | validate = validate_form(params) 19 | 20 | 21 | @route_log.route('/QueryLogByParam', methods=['POST'], endpoint='QueryLogByParam') 22 | @auth.login_required 23 | @validate_current_access 24 | @validate.form('Query') 25 | def QueryLogByParam(): 26 | lists = [ 27 | {'name': 'type', 'key': 'type[]'}, 28 | {'name': 'status', 'key': 'status[]'} 29 | ] 30 | params = {} 31 | for i in lists: 32 | if request.form.getlist(i['key']): 33 | params[i['name']] = request.form.getlist(i['key']) 34 | 35 | result = LogModel().QueryLogByParamRequest( 36 | params=params, 37 | page=int(request.form.get('page', 1)), 38 | page_size=int(request.form.get('page_size', 20)) 39 | ) 40 | 41 | if type(result).__name__ == 'str': 42 | return ResultDeal(msg=result, code=-1) 43 | 44 | return ResultDeal(data=result) 45 | -------------------------------------------------------------------------------- /routes/v1/menu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 菜单API 5 | @Author: Zpp 6 | @Date: 2019-09-10 16:16:54 7 | @LastEditTime: 2020-05-29 14:20:36 8 | @LastEditors: Zpp 9 | ''' 10 | from flask import Blueprint, request 11 | from collection.v1.menu import MenuModel 12 | from ..token_auth import auth, validate_current_access 13 | from libs.code import ResultDeal 14 | from validate import validate_form 15 | from validate.v1.menu import params 16 | 17 | route_menu = Blueprint('Menu', __name__, url_prefix='/v1/Menu') 18 | validate = validate_form(params) 19 | 20 | 21 | @route_menu.route('/CreateMenu', methods=['POST'], endpoint='CreateMenu') 22 | @auth.login_required 23 | @validate_current_access 24 | @validate.form('Create') 25 | def CreateMenu(): 26 | result = MenuModel().CreateMenuRequest(request.form) 27 | 28 | if type(result).__name__ == 'str': 29 | return ResultDeal(msg=result, code=-1) 30 | 31 | return ResultDeal(data=result) 32 | 33 | 34 | @route_menu.route('/DelMenu', methods=['POST'], endpoint='DelMenu') 35 | @auth.login_required 36 | @validate_current_access 37 | @validate.form('Del') 38 | def DelMenu(): 39 | result = MenuModel().DelMenuRequest(request.form.get('menu_id')) 40 | 41 | if type(result).__name__ == 'str': 42 | return ResultDeal(msg=result, code=-1) 43 | 44 | return ResultDeal(data=result) 45 | 46 | 47 | @route_menu.route('/GetMenuToInterface', methods=['GET'], endpoint='GetMenuToInterface') 48 | @auth.login_required 49 | @validate_current_access 50 | @validate.form('Get') 51 | def GetMenuToInterface(): 52 | result = MenuModel().GetMenuToInterfaceRequest(request.args.get('menu_id')) 53 | 54 | if type(result).__name__ == 'str': 55 | return ResultDeal(msg=result, code=-1) 56 | 57 | return ResultDeal(data=result) 58 | 59 | 60 | @route_menu.route('/ModifyMenu', methods=['POST'], endpoint='ModifyMenu') 61 | @auth.login_required 62 | @validate_current_access 63 | @validate.form('Modify') 64 | def ModifyMenu(): 65 | result = MenuModel().ModifyMenuRequest(request.form.get('menu_id'), request.form) 66 | 67 | if type(result).__name__ == 'str': 68 | return ResultDeal(msg=result, code=-1) 69 | 70 | return ResultDeal(data=result) 71 | 72 | 73 | @route_menu.route('/QueryMenuByParam', methods=['POST'], endpoint='QueryMenuByParam') 74 | @auth.login_required 75 | @validate_current_access 76 | @validate.form('Query') 77 | def QueryMenuByParam(): 78 | params = {} 79 | if request.form.get('disable') != None: 80 | params['disable'] = request.form.get('disable') 81 | if request.form.get('role_id'): 82 | params['role_id'] = request.form.get('role_id') 83 | 84 | result = MenuModel().QueryMenuByParamRequest( 85 | params=params, 86 | is_interface=request.form.get('is_interface') 87 | ) 88 | 89 | if type(result).__name__ == 'str': 90 | return ResultDeal(msg=result, code=-1) 91 | 92 | return ResultDeal(data=result) 93 | -------------------------------------------------------------------------------- /routes/v1/role.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*-\ 3 | ''' 4 | @Description: 鉴权API 5 | @Author: Zpp 6 | @Date: 2019-09-12 10:30:39 7 | LastEditTime: 2020-11-26 11:10:34 8 | LastEditors: Zpp 9 | ''' 10 | from flask import Blueprint, request 11 | from collection.v1.role import RoleModel 12 | from ..token_auth import auth, validate_current_access 13 | from libs.code import ResultDeal 14 | from validate import validate_form 15 | from validate.v1.role import params 16 | import json 17 | 18 | route_role = Blueprint('Role', __name__, url_prefix='/v1/Role') 19 | validate = validate_form(params) 20 | 21 | 22 | @route_role.route('/CreateRole', methods=['POST'], endpoint='CreateRole') 23 | @auth.login_required 24 | @validate_current_access 25 | @validate.form('Create') 26 | def CreateRole(): 27 | result = RoleModel().CreateRoleRequest(request.form) 28 | 29 | if type(result).__name__ == 'str': 30 | return ResultDeal(msg=result, code=-1) 31 | 32 | return ResultDeal(data=result) 33 | 34 | 35 | @route_role.route('/LockRole', methods=['POST'], endpoint='LockRole') 36 | @auth.login_required 37 | @validate_current_access 38 | @validate.form('Lock') 39 | def LockRole(): 40 | result = RoleModel().LockRoleRequest( 41 | role_id=request.form.getlist('role_id[]'), 42 | disable=request.form.get('disable') 43 | ) 44 | 45 | if type(result).__name__ == 'str': 46 | return ResultDeal(msg=result, code=-1) 47 | 48 | return ResultDeal(data=result) 49 | 50 | 51 | @route_role.route('/DelRole', methods=['POST'], endpoint='DelRole') 52 | @auth.login_required 53 | @validate_current_access 54 | @validate.form('Del') 55 | def DelRole(): 56 | result = RoleModel().DelRoleRequest( 57 | role_id=request.form.getlist('role_id[]') 58 | ) 59 | 60 | if type(result).__name__ == 'str': 61 | return ResultDeal(msg=result, code=-1) 62 | 63 | return ResultDeal(data=result) 64 | 65 | 66 | @route_role.route('/ModifyRole', methods=['POST'], endpoint='ModifyRole') 67 | @auth.login_required 68 | @validate_current_access 69 | @validate.form('Modify') 70 | def ModifyRole(): 71 | result = RoleModel().ModifyRoleRequest(request.form.get('role_id'), request.form) 72 | 73 | if type(result).__name__ == 'str': 74 | return ResultDeal(msg=result, code=-1) 75 | 76 | return ResultDeal(data=result) 77 | 78 | 79 | @route_role.route('/QueryRoleByParam', methods=['POST'], endpoint='QueryRoleByParam') 80 | @auth.login_required 81 | @validate_current_access 82 | @validate.form('Query') 83 | def QueryRoleByParam(): 84 | result = RoleModel().QueryRoleByParamRequest(params=request.form) 85 | 86 | if type(result).__name__ == 'str': 87 | return ResultDeal(msg=result, code=-1) 88 | 89 | return ResultDeal(data=result) 90 | -------------------------------------------------------------------------------- /services/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-04 10:23:44 7 | LastEditTime: 2020-11-24 16:06:11 8 | LastEditors: Zpp 9 | ''' 10 | from flask_session import Session 11 | from flask_compress import Compress 12 | from flask_cache import Cache 13 | from conf.setting import token_info, session_info, cache_info, _config 14 | from libs.utils import IsWindows 15 | 16 | cache = Cache(with_jinja2_ext=False) 17 | 18 | 19 | def init_app(app): 20 | # 密钥 21 | app.config['SECRET_KEY'] = token_info['SECRET_KEY'] 22 | # 调试模式 23 | app.config['DEBUG'] = IsWindows() 24 | # SESSION配置 25 | app.config.update(session_info) 26 | # CACHE配置 27 | app.config.update(cache_info) 28 | 29 | Session(app) 30 | Compress(app) 31 | global cache 32 | cache.init_app(app) 33 | -------------------------------------------------------------------------------- /sockets/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: SocketIO配置 5 | @Author: Zpp 6 | @Date: 2020-05-19 14:43:38 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-21 16:12:45 9 | ''' 10 | from flask import request 11 | from flask_socketio import emit, Namespace 12 | 13 | client = [] 14 | 15 | 16 | class MyCustomNamespace(Namespace): 17 | def on_error(self, e): 18 | print(e) 19 | 20 | def on_error_default(self, e): 21 | print(e) 22 | 23 | def on_connect(self): 24 | client.append(request.sid) 25 | print(client) 26 | emit('my_response', '连接成功') 27 | 28 | def on_disconnect(self): 29 | sid = request.sid 30 | if sid in client: 31 | client.remove(sid) 32 | print(client) 33 | 34 | def on_heart(self, data): 35 | print(data) 36 | emit('heart', 'server') 37 | 38 | def on_my_response(self, data): 39 | print(data) 40 | 41 | 42 | def init_app(socketio): 43 | socketio.on_namespace(MyCustomNamespace('/')) 44 | -------------------------------------------------------------------------------- /start.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 5 | @Author: Zpp 6 | @Date: 2019-09-05 16:07:19 7 | LastEditTime: 2022-06-30 15:47:51 8 | LastEditors: Zpp 9 | ''' 10 | from flask import Flask 11 | from flask_cors import CORS 12 | from sqlalchemy import Float 13 | # from flask_socketio import SocketIO 14 | from conf.setting import server_info 15 | from libs.utils import IsWindows 16 | import models 17 | import routes 18 | import services 19 | import logs 20 | import logging 21 | 22 | 23 | def create_app(): 24 | app = Flask(__name__) 25 | CORS( 26 | app, 27 | origins=['http://localhost:5001', 'https://test.ig132n.cn'], 28 | supports_credentials=True, 29 | methods='GET, POST' 30 | ) 31 | models.init_app(app) 32 | routes.init_app(app) 33 | services.init_app(app) 34 | return app 35 | 36 | 37 | logs.init_app() 38 | # 初始化 39 | logging.info('-----初始化项目-----') 40 | app = create_app() 41 | # socketio = SocketIO(app) 42 | # sockets.init_app(socketio) 43 | logging.info('--------------------') 44 | 45 | try: 46 | logging.info('------启动成功------') 47 | # if IsWindows(): 48 | # socketio.run(app, host=server_info['host'], port=server_info['port']) 49 | # else: 50 | # socketio.run(app) 51 | app.run(debug=IsWindows(), host=server_info['host'], port=server_info['port']) 52 | except Exception as e: 53 | logging.info(e) 54 | logging.error('------启动失败------') 55 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/favicon.ico -------------------------------------------------------------------------------- /static/image/markdown/admin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/admin.jpg -------------------------------------------------------------------------------- /static/image/markdown/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/desktop.png -------------------------------------------------------------------------------- /static/image/markdown/interface.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/interface.jpg -------------------------------------------------------------------------------- /static/image/markdown/interface_edit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/interface_edit.jpg -------------------------------------------------------------------------------- /static/image/markdown/log.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/log.jpg -------------------------------------------------------------------------------- /static/image/markdown/markdown.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/markdown.jpg -------------------------------------------------------------------------------- /static/image/markdown/menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/menu.jpg -------------------------------------------------------------------------------- /static/image/markdown/role_edit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/role_edit.jpg -------------------------------------------------------------------------------- /static/image/markdown/upload.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huzidabanzhang/python-admin/62fe4b3e264176bb582a278c81814ed5ec13caec/static/image/markdown/upload.jpg -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 测试脚本 5 | @Author: Zpp 6 | @Date: 2020-04-24 14:55:37 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-25 15:42:47 9 | ''' 10 | 11 | 12 | if __name__ == '__main__': 13 | import geoip2.database 14 | import os 15 | 16 | root = os.path.abspath(os.path.dirname(__file__) + '/..') 17 | 18 | reader = geoip2.database.Reader(os.path.join(root, 'tools/GeoLite2-City.mmdb')) 19 | 20 | response = reader.city('116.234.9.196') 21 | print(response.continent.names["zh-CN"]) 22 | print(response.country.names["zh-CN"]) 23 | print(response.subdivisions.most_specific.names["zh-CN"]) 24 | print(response.city.names["zh-CN"]) 25 | print(response.location.longitude) 26 | print(response.location.latitude) 27 | -------------------------------------------------------------------------------- /tools/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 数据库迁移 5 | @Author: Zpp 6 | @Date: 2020-03-30 11:01:56 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-04-28 09:55:26 9 | ''' 10 | import sys 11 | import os 12 | curPath = os.path.abspath(os.path.dirname(__file__)) 13 | rootPath = os.path.split(curPath)[0] 14 | sys.path.append(rootPath) 15 | 16 | from flask import Flask 17 | from flask_sqlalchemy import SQLAlchemy 18 | from flask_script import Manager 19 | from flask_migrate import Migrate, MigrateCommand 20 | from conf.setting import Config 21 | 22 | app = Flask(__name__) 23 | app.config['SQLALCHEMY_DATABASE_URI'] = Config().get_sql_url() 24 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 25 | 26 | db = SQLAlchemy(app) 27 | 28 | from models.salary import * 29 | from models.system import * 30 | from models.log import * 31 | 32 | # 初始化 migrate 33 | # 两个参数一个是 Flask 的 app,一个是数据库 db 34 | migrate = Migrate(app, db) 35 | 36 | # 初始化管理器 37 | manager = Manager(app) 38 | # 添加 db 命令,并与 MigrateCommand 绑定 39 | manager.add_command('db', MigrateCommand) 40 | 41 | 42 | if __name__ == '__main__': 43 | manager.run() 44 | -------------------------------------------------------------------------------- /validate/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 验证器 5 | @Author: Zpp 6 | @Date: 2020-04-20 13:43:42 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-24 16:39:20 9 | ''' 10 | from flask import request 11 | from libs.code import ResultDeal 12 | import re 13 | import datetime 14 | 15 | 16 | class validate_form(): 17 | ''' 18 | 表单验证器 19 | ''' 20 | 21 | def __init__(self, params): 22 | self.params = params 23 | self.phone = re.compile('^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-7|9])|(?:5[0-3|5-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1|8|9]))\d{8}$') 24 | self.id_card = re.compile('^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}[\dXx]$') 25 | self.email = re.compile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") 26 | 27 | def validate_min_max(self, value, i): 28 | ''' 29 | 验证字段最大最小值 30 | ''' 31 | error = None 32 | 33 | if 'max' in i: 34 | if (value if not type(value) in [list, str] else len(value)) > i['max']: 35 | error = '%s最多不能超过%s个' % (i['name'], i['max']) 36 | 37 | if 'min' in i: 38 | if (value if not type(value) in [list, str] else len(value)) < i['min']: 39 | error = '%s不能少于%s个' % (i['name'], i['min']) 40 | 41 | return error 42 | 43 | def validate_between(self, value, i): 44 | ''' 45 | 验证字段是否在固定值之间 46 | ''' 47 | error = None 48 | 49 | if type(value) != list and 'between' in i and type(i['between']) == list and len(i['between']) == 2: 50 | if not value in i['between']: 51 | error = '%s只能在%s和%s之间' % (i['name'], i['between'][0], i['between'][1]) 52 | 53 | return error 54 | 55 | def validate_params(self, value, i, fun): 56 | ''' 57 | 验证字段 58 | ''' 59 | error = None 60 | 61 | if i['type'] == 'int': 62 | if type(value) != int: 63 | error = True 64 | 65 | if i['type'] == 'str': 66 | if type(value) != str: 67 | error = True 68 | 69 | if i['type'] == 'boolean': 70 | if value != 'true' and value != 'false': 71 | error = True 72 | else: 73 | self.boolean_change(value, i['field']) 74 | 75 | if i['type'] == 'list': 76 | if type(value) != list: 77 | error = True 78 | 79 | if i['type'] == 'ic': 80 | if not self.id_card.match(value): 81 | error = True 82 | 83 | if i['type'] == 'phone': 84 | if not self.phone.match(value): 85 | error = True 86 | 87 | if i['type'] == 'email': 88 | if not self.email.match(value): 89 | error = True 90 | 91 | if i['type'] == 'time': 92 | if type(value) != datetime.datetime: 93 | error = True 94 | 95 | if i['type'] != 'boolean': 96 | error = self.validate_min_max(value, i) 97 | if not error: 98 | error = self.validate_between(value, i) 99 | 100 | if error == True: 101 | return ResultDeal(code=-1, msg='%s格式错误' % i['name']) 102 | elif error == None: 103 | if 'default' in i and not value: 104 | self.add_default(i['default'], i['field']) 105 | else: 106 | return ResultDeal(code=-1, msg=error) 107 | 108 | def add_default(self, default, f): 109 | ''' 110 | 添加默认值 111 | ''' 112 | r = request.form.copy() 113 | r[f] = default 114 | request.form = r 115 | 116 | def boolean_change(self, value, f): 117 | ''' 118 | 前端布尔值改为py的布尔值 119 | ''' 120 | r = request.form.copy() 121 | r[f] = True if value == 'true' else False 122 | request.form = r 123 | 124 | def get_data(self, t, f): 125 | ''' 126 | 获取值 127 | ''' 128 | if t == 'list': 129 | return request.form.getlist(f) 130 | 131 | if t == 'file': 132 | return request.files.get(f) 133 | 134 | if t == 'files': 135 | return request.files.getlist(f) 136 | 137 | if request.method == 'GET': 138 | return request.args.get(f) 139 | else: 140 | return request.form.get(f) 141 | 142 | def get_field(self, value, params=None): 143 | ''' 144 | 获取字段详情 145 | ''' 146 | data = None 147 | if value in self.params['fields']: 148 | data = self.params['fields'][value] 149 | if params: 150 | for i in params: 151 | data[i] = params[i] 152 | 153 | return data 154 | 155 | def form(self, args): 156 | ''' 157 | 验证表单字段 158 | ''' 159 | def validate(f): 160 | def one(): 161 | try: 162 | if args in self.params: 163 | for i in self.params[args]: 164 | field = None 165 | 166 | if type(i) == dict: 167 | if 'field' in i: 168 | field = self.get_field(i['field'], i) 169 | else: 170 | field = self.get_field(i) 171 | field['field'] = i 172 | 173 | if not field: 174 | continue 175 | else: 176 | value = self.get_data(field['type'], field['field']) 177 | 178 | if 'required' not in field or not field['required']: 179 | if value: 180 | self.validate_params(value, field, f) 181 | else: 182 | if 'default' in field: 183 | self.add_default(field['default'], field['field']) 184 | else: 185 | if not value: 186 | if 'default' in field: 187 | self.add_default(field['default'], field['field']) 188 | else: 189 | if 'msg' in field: 190 | return ResultDeal(code=-1, msg=field['msg']) 191 | else: 192 | return ResultDeal(code=-1, msg='请填写%s' % field['name']) 193 | else: 194 | self.validate_params(value, field, f) 195 | 196 | return f() 197 | except Exception as e: 198 | print(e) 199 | return ResultDeal(code=-1, msg=str(e)) 200 | return one 201 | 202 | return validate 203 | -------------------------------------------------------------------------------- /validate/v1/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Description: 3 | @Author: Zpp 4 | @Date: 2020-05-28 09:49:05 5 | @LastEditors: Zpp 6 | @LastEditTime: 2020-05-28 09:49:06 7 | ''' 8 | -------------------------------------------------------------------------------- /validate/v1/admin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 管理员验证器 5 | @Author: Zpp 6 | @Date: 2020-05-27 16:56:52 7 | LastEditors: Zpp 8 | LastEditTime: 2022-03-22 08:55:38 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'code': { 15 | 'name': '验证码', 16 | 'type': 'str', 17 | 'min': 4, 18 | 'max': 4, 19 | 'required': True 20 | }, 21 | 'username': { 22 | 'name': '用户名', 23 | 'type': 'str', 24 | 'min': 2, 25 | 'required': True 26 | }, 27 | 'password': { 28 | 'name': '密码', 29 | 'type': 'str', 30 | 'min': 6, 31 | 'required': True 32 | }, 33 | 'nickname': { 34 | 'name': '昵称', 35 | 'type': 'str', 36 | 'default': '' 37 | }, 38 | 'email': { 39 | 'name': '邮件', 40 | 'type': 'email', 41 | 'default': '' 42 | }, 43 | 'sex': { 44 | 'name': '性别', 45 | 'type': 'int', 46 | 'default': 1 47 | }, 48 | 'role_id': { 49 | 'name': '角色', 50 | 'type': 'str', 51 | 'required': True 52 | }, 53 | 'disable': { 54 | 'name': '可见性', 55 | 'type': 'boolean', 56 | 'required': True 57 | }, 58 | 'avatar': { 59 | 'name': '头像', 60 | 'type': 'str', 61 | 'default': '' 62 | }, 63 | 'admin_id': { 64 | 'name': '管理员编号', 65 | 'type': 'str', 66 | 'required': True 67 | }, 68 | 'admin_id[]': { 69 | 'name': '管理员编号', 70 | 'type': 'list', 71 | 'required': True 72 | }, 73 | 'page': { 74 | 'name': '页码', 75 | 'type': 'int', 76 | 'default': 1 77 | }, 78 | 'page_size': { 79 | 'name': '条数', 80 | 'type': 'int', 81 | 'default': 20 82 | }, 83 | 'order_by': { 84 | 'name': '排序字段', 85 | 'type': 'str', 86 | 'default': None 87 | } 88 | }, 89 | # 登录验证 90 | 'Login': ['code', 'username', 'password'], 91 | # 创建管理员 92 | 'Create': ['username', 'password', 'nickname', 'email', 'sex', 'role_id', 'disable', 'avatar'], 93 | # 编辑管理员 94 | 'Modify': ['admin_id', 'password', 'nickname', 'email', 'sex', 'role_id', 'disable', 'avatar'], 95 | # 禁用管理员 96 | 'Lock': ['admin_id[]', 'disable'], 97 | # 删除管理员 98 | 'Del': ['admin_id[]'], 99 | 'Query': [{ 100 | 'field': 'disable', 101 | 'required': False 102 | }, { 103 | 'field': 'role_id', 104 | 'required': False 105 | }, 'page', 'page_size', 'order_by'] 106 | } 107 | -------------------------------------------------------------------------------- /validate/v1/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 数据库验证器 5 | @Author: Zpp 6 | @Date: 2020-05-28 13:44:29 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-28 14:02:02 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'type': { 15 | 'name': '导出类型', 16 | 'type': 'int', 17 | 'between': [1, 2, 3], 18 | 'required': True 19 | }, 20 | 'document': { 21 | 'name': '数据库文件', 22 | 'type': 'file', 23 | 'required': True, 24 | 'msg': '请选择上传数据库文件' 25 | }, 26 | 'admin_id': { 27 | 'name': '管理员编号', 28 | 'type': 'str', 29 | 'required': True 30 | }, 31 | 'time': { 32 | 'name': '查询时间', 33 | 'type': 'str', 34 | 'required': True 35 | } 36 | }, 37 | # 导出数据库 38 | 'Export': ['type'], 39 | # 导入数据库 40 | 'Import': ['document'], 41 | # 首页登录清空 42 | 'Login': ['admin_id', 'time'] 43 | } 44 | -------------------------------------------------------------------------------- /validate/v1/document.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 附件验证器 5 | @Author: Zpp 6 | @Date: 2020-05-28 13:56:03 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-28 15:05:21 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'document': { 15 | 'name': '文件', 16 | 'type': 'files', 17 | 'required': True, 18 | 'msg': '请选择上传文件' 19 | }, 20 | 'admin_id': { 21 | 'name': '管理员编号', 22 | 'type': 'str', 23 | 'required': True 24 | }, 25 | 'uid[]': { 26 | 'name': '文件编号', 27 | 'type': 'list', 28 | 'required': True 29 | }, 30 | 'status': { 31 | 'name': '类型', 32 | 'type': 'int', 33 | 'required': True 34 | }, 35 | 'folder_id': { 36 | 'name': '所属文件夹', 37 | 'type': 'str' 38 | }, 39 | 'document_id[]': { 40 | 'name': '附件编号', 41 | 'type': 'list', 42 | 'required': True 43 | }, 44 | 'deleted': { 45 | 'name': '是否放入回收站', 46 | 'type': 'boolean', 47 | 'required': True 48 | }, 49 | 'page': { 50 | 'name': '页码', 51 | 'type': 'int', 52 | 'default': 1 53 | }, 54 | 'page_size': { 55 | 'name': '条数', 56 | 'type': 'int', 57 | 'default': 20 58 | }, 59 | 'order_by': { 60 | 'name': '排序字段', 61 | 'type': 'str', 62 | 'default': None 63 | } 64 | }, 65 | # 创建附件 66 | 'Create': ['admin_id', 'status', 'uid[]', { 67 | 'field': 'folder_id', 68 | 'default': None 69 | }, 'document'], 70 | # 放入回收站 71 | 'Retrieve': ['document_id[]', 'deleted'], 72 | # 查询附件列表 73 | 'Query': [{ 74 | 'field': 'status', 75 | 'required': False 76 | }, { 77 | 'field': 'deleted', 78 | 'required': False 79 | }, 'folder_id', 'page', 'page_size', 'order_by'], 80 | # 删除附件 81 | 'Del': ['document_id[]'] 82 | } 83 | -------------------------------------------------------------------------------- /validate/v1/folder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 文件夹验证器 5 | @Author: Zpp 6 | @Date: 2020-05-29 11:28:26 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-29 14:40:01 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'name': { 15 | 'name': '文件夹名称', 16 | 'type': 'str', 17 | 'required': True 18 | }, 19 | 'admin_id': { 20 | 'name': '管理员编号', 21 | 'type': 'str', 22 | 'required': True 23 | }, 24 | 'pid': { 25 | 'name': '父编号', 26 | 'type': 'str', 27 | 'required': True, 28 | 'default': '0' 29 | }, 30 | 'is_sys': { 31 | 'name': '是否是系统文件夹', 32 | 'type': 'boolean', 33 | 'required': True 34 | }, 35 | 'folder_id': { 36 | 'name': '所属文件夹', 37 | 'type': 'str', 38 | 'required': True 39 | } 40 | }, 41 | 'Create': ['admin_id', 'name', 'pid', 'is_sys'], 42 | 'Modify': ['folder_id', 'name', { 43 | 'field': 'pid', 44 | 'required': False 45 | }], 46 | 'Query': ['pid', 'admin_id'], 47 | 'Del': ['folder_id'] 48 | } 49 | -------------------------------------------------------------------------------- /validate/v1/interface.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 接口验证器 5 | @Author: Zpp 6 | @Date: 2020-05-29 16:29:31 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-26 14:13:53 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'name': { 15 | 'name': '接口名称', 16 | 'type': 'str', 17 | 'required': True 18 | }, 19 | 'path': { 20 | 'name': '路径', 21 | 'type': 'str', 22 | 'required': True 23 | }, 24 | 'method': { 25 | 'name': '请求方式', 26 | 'type': 'str', 27 | 'between': ['GET', 'POST', 'PUT', 'DELETE'], 28 | 'required': True 29 | }, 30 | 'mark': { 31 | 'name': '菜单标识', 32 | 'type': 'str', 33 | 'required': True 34 | }, 35 | 'description': { 36 | 'name': '描述', 37 | 'type': 'str', 38 | 'required': True 39 | }, 40 | 'menus[]': { 41 | 'name': '所属菜单', 42 | 'type': 'list', 43 | 'required': True 44 | }, 45 | 'roles[]': { 46 | 'name': '角色集', 47 | 'type': 'list', 48 | 'required': True 49 | }, 50 | 'forbid': { 51 | 'name': '可隐藏', 52 | 'type': 'boolean', 53 | 'required': True 54 | }, 55 | 'disable': { 56 | 'name': '可见性', 57 | 'type': 'boolean', 58 | 'required': True 59 | }, 60 | 'interface_id[]': { 61 | 'name': '接口编号', 62 | 'type': 'list', 63 | 'required': True 64 | }, 65 | 'interface_id': { 66 | 'name': '接口编号', 67 | 'type': 'str', 68 | 'required': True 69 | }, 70 | 'page': { 71 | 'name': '页码', 72 | 'type': 'int', 73 | 'default': 1 74 | }, 75 | 'page_size': { 76 | 'name': '条数', 77 | 'type': 'int', 78 | 'default': 20 79 | }, 80 | 'order_by': { 81 | 'name': '排序字段', 82 | 'type': 'str', 83 | 'default': None 84 | }, 85 | 'menu_id': { 86 | 'name': '菜单', 87 | 'type': 'str' 88 | }, 89 | 'role_id': { 90 | 'name': '角色', 91 | 'type': 'str' 92 | } 93 | }, 94 | 'Create': ['name', 'path', 'method', 'description', 'menus[]', 'mark', 'forbid', 'disable', 'roles[]'], 95 | 'Modify': ['interface_id', 'name', 'path', 'method', 'description', 'menus[]', 'mark', 'forbid', 'disable', 'roles[]'], 96 | 'Del': ['interface_id[]'], 97 | 'Lock': ['interface_id[]', 'disable'], 98 | 'Query': [{ 99 | 'field': 'disable', 100 | 'required': False 101 | }, { 102 | 'field': 'name', 103 | 'required': False 104 | }, { 105 | 'field': 'method', 106 | 'required': False 107 | }, 'page', 'page_size', 'order_by', 'menu_id', 'role_id'] 108 | } 109 | -------------------------------------------------------------------------------- /validate/v1/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 日志验证器 5 | @Author: Zpp 6 | @Date: 2020-05-29 14:00:21 7 | @LastEditors: Zpp 8 | @LastEditTime: 2020-05-29 14:32:18 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'type[]': { 15 | 'name': '日志类型', 16 | 'type': 'list' 17 | }, 18 | 'status[]': { 19 | 'name': '日志状态', 20 | 'type': 'list' 21 | }, 22 | 'page': { 23 | 'name': '页码', 24 | 'type': 'int', 25 | 'default': 1 26 | }, 27 | 'page_size': { 28 | 'name': '条数', 29 | 'type': 'int', 30 | 'default': 20 31 | } 32 | }, 33 | 'Query': ['type[]', 'status[]', 'page', 'page_size'] 34 | } 35 | -------------------------------------------------------------------------------- /validate/v1/menu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 菜单验证器 5 | @Author: Zpp 6 | @Date: 2020-05-29 14:08:17 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-26 11:08:16 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'pid': { 15 | 'name': '父编号', 16 | 'type': 'str', 17 | 'required': True, 18 | 'default': '0' 19 | }, 20 | 'title': { 21 | 'name': '菜单名称', 22 | 'type': 'str', 23 | 'required': True 24 | }, 25 | 'name': { 26 | 'name': '路由名称', 27 | 'type': 'str', 28 | 'required': True 29 | }, 30 | 'path': { 31 | 'name': '路径', 32 | 'type': 'str', 33 | 'required': True 34 | }, 35 | 'icon': { 36 | 'name': '菜单图标', 37 | 'type': 'str', 38 | 'required': True 39 | }, 40 | 'mark': { 41 | 'name': '菜单标识', 42 | 'type': 'str', 43 | 'required': True 44 | }, 45 | 'sort': { 46 | 'name': '排序', 47 | 'type': 'int', 48 | 'required': True 49 | }, 50 | 'component': { 51 | 'name': '路由组件', 52 | 'type': 'str', 53 | 'required': True 54 | }, 55 | 'componentPath': { 56 | 'name': '组件路径', 57 | 'type': 'str', 58 | 'required': True 59 | }, 60 | 'disable': { 61 | 'name': '可见性', 62 | 'type': 'boolean', 63 | 'required': True 64 | }, 65 | 'cache': { 66 | 'name': '路由缓存', 67 | 'type': 'boolean', 68 | 'required': True 69 | }, 70 | 'menu_id': { 71 | 'name': '菜单编号', 72 | 'type': 'str', 73 | 'required': True 74 | }, 75 | 'role_id': { 76 | 'name': '角色', 77 | 'type': 'str', 78 | 'required': True 79 | }, 80 | 'is_interface': { 81 | 'name': '是否带API', 82 | 'type': 'boolean' 83 | }, 84 | 'roles[]': { 85 | 'name': '角色集', 86 | 'type': 'list', 87 | 'required': True 88 | } 89 | }, 90 | 'Create': ['pid', 'title', 'path', 'icon', 'mark', 'sort', 'component', 'componentPath', 'name', 'cache', 'disable', 'roles[]'], 91 | 'Get': ['menu_id'], 92 | 'Modify': ['menu_id', 'pid', 'title', 'path', 'icon', 'mark', 'sort', 'component', 'componentPath', 'name', 'cache', 'disable', 'roles[]'], 93 | 'Del': ['menu_id'], 94 | 'Query': ['is_interface', { 95 | 'field': 'disable', 96 | 'required': False 97 | }, { 98 | 'field': 'role_id', 99 | 'required': False 100 | }] 101 | } 102 | -------------------------------------------------------------------------------- /validate/v1/role.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | ''' 4 | @Description: 角色验证器 5 | @Author: Zpp 6 | @Date: 2020-05-29 14:21:43 7 | LastEditors: Zpp 8 | LastEditTime: 2020-11-26 11:09:27 9 | ''' 10 | 11 | params = { 12 | # 验证字段 13 | 'fields': { 14 | 'name': { 15 | 'name': '角色名称', 16 | 'type': 'str', 17 | 'required': True 18 | }, 19 | 'mark': { 20 | 'name': '角色标识', 21 | 'type': 'str', 22 | 'required': True 23 | }, 24 | 'disable': { 25 | 'name': '可见性', 26 | 'type': 'boolean', 27 | 'required': True 28 | }, 29 | 'interface[]': { 30 | 'name': 'API列表', 31 | 'type': 'list', 32 | 'required': True 33 | }, 34 | 'menu[]': { 35 | 'name': '菜单列表', 36 | 'type': 'list', 37 | 'required': True 38 | }, 39 | 'role_id[]': { 40 | 'name': '角色编号', 41 | 'type': 'list', 42 | 'required': True 43 | }, 44 | 'role_id': { 45 | 'name': '角色编号', 46 | 'type': 'str', 47 | 'required': True 48 | }, 49 | 'is_default': { 50 | 'name': '获取系统角色Id', 51 | 'type': 'boolean' 52 | } 53 | }, 54 | 'Create': ['name', 'mark', 'interface[]', 'disable', 'menu[]'], 55 | 'Modify': ['role_id', 'name', 'mark', 'interface[]', 'disable', 'menu[]'], 56 | 'Lock': ['role_id[]', 'disable'], 57 | 'Del': ['role_id[]'], 58 | 'Query': [{ 59 | 'field': 'disable', 60 | 'required': False 61 | }, 'is_default'] 62 | } 63 | --------------------------------------------------------------------------------