├── .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 | 
106 |
107 | 
108 |
109 | 
110 |
111 | 
112 |
113 | 
114 |
115 | 
116 |
117 | 
118 |
119 | 
120 |
121 | 
122 |
123 | 
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 '