功能变化(全局):拉取其他分支代码;

pull/19/head
xt12321 2021-04-03 10:15:49 +08:00
commit 70588e3d5b
89 changed files with 770 additions and 1254 deletions

View File

@ -1,6 +1,6 @@
# Django-Vue-Admin # Django-Vue-Admin
[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [![img](https://img.shields.io/pypi/v/django-simpleui.svg)](https://pypi.org/project/django-simpleui/#history) [![img](https://img.shields.io/badge/python-%3E=3.6.x-green.svg)](https://python.org/) ![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-2.2-blue)[![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/download/releases/)[![img](https://img.shields.io/pypi/dm/django-simpleui.svg)](https://pypi.org/project/django-simpleui/) [![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [![img](https://img.shields.io/pypi/v/django-simpleui.svg)](https://pypi.org/project/django-simpleui/#history) [![img](https://img.shields.io/badge/python-%3E=3.6.x-green.svg)](https://python.org/) ![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-2.2-blue)![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)
@ -12,26 +12,25 @@ Django-Vue-Admin 是一套全部开源的快速开发平台,毫无保留给个
* 后端采用Python语言Django框架。 * 后端采用Python语言Django框架。
* 权限认证使用Jwt支持多终端认证系统。 * 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。 * 支持加载动态权限菜单,多方式轻松权限控制。
* ~~高效率开发,使用代码生成器可以一键生成前后端代码。~~ * 特别鸣谢:<u>[Gin-Vue-Admin](https://www.gin-vue-admin.com/)</u>[RuoYi](https://gitee.com/y_project/RuoYi-Vue) [Vue-Element-Admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://gitee.com/elunez/eladmin-web?_from=gitee_search)。
* 特别鸣谢:[RuoYi](https://gitee.com/y_project/RuoYi-Vue) [Vue-Element-Admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://gitee.com/elunez/eladmin-web?_from=gitee_search)[Gin-Vue-Admin](https://www.gin-vue-admin.com/)。
## QQ群 ## QQ群
- QQ群号812482043 - QQ群号812482043
- 由于项目正在启步阶段第一版预计3月底发后序会慢慢维护其他版本有什么不到位的请大家担待~ - 二维码
<img src='https://gitee.com/liqianglog/django-vue-admin/raw/master/dvadmin-ui/src/assets/images/qq.jpg' width='200'> <img src='https://gitee.com/liqianglog/django-vue-admin/raw/master/dvadmin-ui/src/assets/images/qq.jpg' width='200'>
## 源码地址 ## 源码地址
gitee地址[https://gitee.com/liqianglog/django-vue-admin](https://gitee.com/liqianglog/django-vue-admin) gitee地址(主推)[https://gitee.com/liqianglog/django-vue-admin](https://gitee.com/liqianglog/django-vue-admin)
github地址[https://github.com/liqianglog/django-vue-admin](https://github.com/liqianglog/django-vue-admin) github地址[https://github.com/liqianglog/django-vue-admin](https://github.com/liqianglog/django-vue-admin)
## 内置功能 ## 内置功能
##### 预计3月底发布v1.0正式版本,个别功能开发中 [版本功能说明](https://gitee.com/liqianglog/django-vue-admin/wikis/releaseNote?sort_id=3615540) ##### 后期版本 [版本功能说明](https://gitee.com/liqianglog/django-vue-admin/wikis/releaseNote?sort_id=3615540)
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
@ -44,11 +43,10 @@ github地址[https://github.com/liqianglog/django-vue-admin](https://github.c
9. 通知公告:发布通知公告给所有人,进行消息的通知。 9. 通知公告:发布通知公告给所有人,进行消息的通知。
10. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 10. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
11. 登录日志:系统登录日志记录查询包含登录异常。 11. 登录日志:系统登录日志记录查询包含登录异常。
12. 在线用户:当前系统中活跃用户状态监控、用户强退功能。 12. 定时日志celery定时任务执行日志记录。
13. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 13. 在线用户:当前系统中活跃用户状态监控、用户强退功能。
14. 用户注册:新用户注册页面。 14. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
15. 服务监控监视当前系统CPU、内存、磁盘、堆栈、celery 当前状态等相关信息。 15. 在线构建器拖动表单元素生成相应的HTML代码。
16. 在线构建器拖动表单元素生成相应的HTML代码。
## 在线体验 ## 在线体验
@ -68,9 +66,6 @@ git clone https://gitee.com/liqianglog/django-vue-admin.git
cd dvadmin-ui cd dvadmin-ui
# 安装依赖 # 安装依赖
npm install
# 建议不要直接使用cnpm安装依赖会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题。
npm install --registry=https://registry.npm.taobao.org npm install --registry=https://registry.npm.taobao.org
# 启动服务 # 启动服务
@ -95,7 +90,12 @@ npm run build:prod
~~~bash ~~~bash
1. 进入项目目录 cd dvadmin-backend 1. 进入项目目录 cd dvadmin-backend
2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py 2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py
3. 在 env.py 中配置数据库信息 3. 在 env.py 中配置数据库信息
mysql数据库版本建议:5.7以上
mysql数据库字符集utf8mb4
mysql数据库排序规则utf8mb4_0900_ai_ci
4. 安装依赖环境 4. 安装依赖环境
pip3 install -r requirements.txt pip3 install -r requirements.txt
5. 执行迁移命令: 5. 执行迁移命令:
@ -104,16 +104,34 @@ npm run build:prod
6. 初始化数据 6. 初始化数据
python3 manage.py init python3 manage.py init
7. 启动项目 7. 启动项目
python3 manage.py runserver 0.0.0.0:8000 python3 manage.py runserver 127.0.0.1:8000
定时任务启动命令: 定时任务启动命令:
celery -A application worker -B --loglevel=info celery -A application worker -B --loglevel=info
注:
Windows 运行celery 需要安装 pip install eventlet
celery -A application worker -P eventlet --loglevel=info
初始账号admin 密码123456 初始账号admin 密码123456
后端接口文档地址http://127.0.0.1:8000/docs/ 后端接口文档地址http://127.0.0.1:8000/docs/
~~~ ~~~
### docker-compose 运行
~~~shell
# 先安装docker-compose (自行百度安装),执行此命令等待安装
docker-compose up
# 初始化后端数据(第一次执行即可)
docker exec -ti dvadmin-django bash
python manage.py init -y
exit
前端地址http://127.0.0.1:8080
后端地址http://127.0.0.1:8000
账号admin 密码123456
~~~
## 演示图 ## 演示图

View File

@ -23,25 +23,7 @@ services:
npm install --registry=https://registry.npm.taobao.org npm install --registry=https://registry.npm.taobao.org
rm -rf /dvadmin-ui/dist rm -rf /dvadmin-ui/dist
npm run build:prod npm run build:prod
npm run dev
dvadmin-doc:
container_name: dvadmin-doc
build:
context: ./
dockerfile: ./docker_env/vue-doc/Dockerfile
environment:
TZ: Asia/Shanghai
volumes:
- "./dvadmin-doc:/dvadmin-doc"
command:
- /bin/bash
- -c
- |
cd /dvadmin-doc
npm install --registry=https://registry.npm.taobao.org
rm -rf /dvadmin-doc/dist
npm run docs:build
dvadmin-redis: dvadmin-redis:
@ -104,6 +86,8 @@ services:
volumes: volumes:
- ./dvadmin-backend:/dvadmin-backend - ./dvadmin-backend:/dvadmin-backend
- ./logs/log:/var/log - ./logs/log:/var/log
env_file:
- ./.env
ports: ports:
- "8000:8000" - "8000:8000"
expose: expose:
@ -135,31 +119,6 @@ services:
networks: networks:
- dvadmin_net - dvadmin_net
dvadmin-nginx:
image: nginx:latest
container_name: dvadmin-nginx
# build: ./docker_env/nginx
restart: always
ports:
- "80:80"
- "443:443"
expose:
- "80"
- "443"
volumes:
- ./docker_env/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./docker_env/nginx/sites-enabled:/etc/nginx/sites-enabled
- ./docker_env/nginx/keys:/nginx/keys
- ./dvadmin-backend:/dvadmin-backend
- ./dvadmin-doc:/dvadmin-doc
- ./dvadmin-ui:/dvadmin-ui
- ./logs/nginx:/var/log/nginx
depends_on:
- dvadmin-django
networks:
- dvadmin_net
networks: networks:
dvadmin_net: dvadmin_net:
driver: bridge driver: bridge

View File

@ -1,6 +0,0 @@
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]

View File

@ -1,61 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFoTCCBImgAwIBAgIQA4TN09XlucVnL4BLCuvHijANBgkqhkiG9w0BAQsFADBy
MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywg
SW5jLjEdMBsGA1UECxMURG9tYWluIFZhbGlkYXRlZCBTU0wxHTAbBgNVBAMTFFRy
dXN0QXNpYSBUTFMgUlNBIENBMB4XDTIxMDMwMzAwMDAwMFoXDTIyMDMwMjIzNTk1
OVowIzEhMB8GA1UEAxMYYXBpLmRqYW5nby12dWUtYWRtaW4uY29tMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu5QSBTQbToLUu4wCYy/BNoinqFkXiTZE
aV6/5PJIeNsz75fnAEuBpIATEHqRsY6L9HdYAvBiAEv6ufCZhzwWF7ph1ZMhg6ul
foaQwoVCkbKi/zgwi3tvteda5vXQs4e8GvgZ6zkabQ4cZFjVpb3dA6huBbs20jLf
YYXsXWsJEGF3JK5okQ08+u/h/q0lFDFa70S9hpQXMtSfCCW/AuEc/+tG7rnUul1o
MXjpVnDOmg+CZfIYgi9D30/zd1DYFJOEwawl5FKLFQY7TOn3RlZ3SR4mNbbhIgHP
L9S1xHGcm8UC7PKuOgh8+5Nl0aeeUB1liuzM/w5JbF4L4FoZW6ciNQIDAQABo4IC
gDCCAnwwHwYDVR0jBBgwFoAUf9OZ86BHDjEAVlYijrfMnt3KAYowHQYDVR0OBBYE
FHvXBhsuPta7SwvCVVgv9e/bZ9AeMCMGA1UdEQQcMBqCGGFwaS5kamFuZ28tdnVl
LWFkbWluLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG
CCsGAQUFBwMCMD4GA1UdIAQ3MDUwMwYGZ4EMAQIBMCkwJwYIKwYBBQUHAgEWG2h0
dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBkgYIKwYBBQUHAQEEgYUwgYIwNAYI
KwYBBQUHMAGGKGh0dHA6Ly9zdGF0dXNlLmRpZ2l0YWxjZXJ0dmFsaWRhdGlvbi5j
b20wSgYIKwYBBQUHMAKGPmh0dHA6Ly9jYWNlcnRzLmRpZ2l0YWxjZXJ0dmFsaWRh
dGlvbi5jb20vVHJ1c3RBc2lhVExTUlNBQ0EuY3J0MAkGA1UdEwQCMAAwggEEBgor
BgEEAdZ5AgQCBIH1BIHyAPAAdgApeb7wnjk5IfBWc59jpXflvld9nGAK+PlNXSZc
JV3HhAAAAXf48Te1AAAEAwBHMEUCIDjqPKTNX4EcFxayaLirTT1y98X9X1HP97bM
IV0HpJmYAiEAjMdYYUqFuHLkSl4/bq5F/FPtt25AbKGhyolIVDWvsNoAdgAiRUUH
WVUkVpY/oS/x922G4CMmY63AS39dxoNcbuIPAgAAAXf48TgFAAAEAwBHMEUCIBQM
bXvVtAOXQfBjp+7HzsyWH51BZ2vyH1VyLpuGSs7dAiEAiyt4h7P7oRheoZKmn8wp
CptaYocY509Dr8aFbCicWWAwDQYJKoZIhvcNAQELBQADggEBAJG3Ne57IlaucGjH
y6j/zJsRnCsIrVNoPUROma+2FD7SauHT6U7iDLtNT5BIrBmDcEnYdqMAjJQoRJC1
VvXSkABEngUajpdpB0m8k9UVB5W+6Extt8TYyRLu1/Xpq0JHiOg+rPYK3UljYybG
2V9KWNhYd2G5yg0+1ZzvaC3zEFIfQJpuESio6hEbilT6FLeVC8TabL0/2tqE12n/
uWYU3Pr9AokD0OQkzN4BUswwIpYBfAXa6HLvj+V6tLOthxY0PLuOm7WiwU2MP0bz
dq6LIFu0z8fkwBkkqXXtgSfCigqvRhybaKKF0FT4cdJUb0cfMhUshlzWpJ5EEskO
46xDUok=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIErjCCA5agAwIBAgIQBYAmfwbylVM0jhwYWl7uLjANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzEyMDgxMjI4MjZaFw0yNzEyMDgxMjI4MjZaMHIxCzAJBgNVBAYTAkNO
MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMR0wGwYDVQQL
ExREb21haW4gVmFsaWRhdGVkIFNTTDEdMBsGA1UEAxMUVHJ1c3RBc2lhIFRMUyBS
U0EgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgWa9X+ph+wAm8
Yh1Fk1MjKbQ5QwBOOKVaZR/OfCh+F6f93u7vZHGcUU/lvVGgUQnbzJhR1UV2epJa
e+m7cxnXIKdD0/VS9btAgwJszGFvwoqXeaCqFoP71wPmXjjUwLT70+qvX4hdyYfO
JcjeTz5QKtg8zQwxaK9x4JT9CoOmoVdVhEBAiD3DwR5fFgOHDwwGxdJWVBvktnoA
zjdTLXDdbSVC5jZ0u8oq9BiTDv7jAlsB5F8aZgvSZDOQeFrwaOTbKWSEInEhnchK
ZTD1dz6aBlk1xGEI5PZWAnVAba/ofH33ktymaTDsE6xRDnW97pDkimCRak6CEbfe
3dXw6OV5AgMBAAGjggFPMIIBSzAdBgNVHQ4EFgQUf9OZ86BHDjEAVlYijrfMnt3K
AYowHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQD
AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG
AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
ZGlnaWNlcnQuY29tMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwTAYDVR0gBEUwQzA3Bglg
hkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29t
L0NQUzAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAK3dVOj5dlv4MzK2i233
lDYvyJ3slFY2X2HKTYGte8nbK6i5/fsDImMYihAkp6VaNY/en8WZ5qcrQPVLuJrJ
DSXT04NnMeZOQDUoj/NHAmdfCBB/h1bZ5OGK6Sf1h5Yx/5wR4f3TUoPgGlnU7EuP
ISLNdMRiDrXntcImDAiRvkh5GJuH4YCVE6XEntqaNIgGkRwxKSgnU3Id3iuFbW9F
UQ9Qqtb1GX91AJ7i4153TikGgYCdwYkBURD8gSVe8OAco6IfZOYt/TEwii1Ivi1C
qnuUlWpsF1LdQNIdfbW3TSe0BhQa7ifbVIfvPWHYOu3rkg1ZeMo6XRU9B4n5VyJY
RmE=
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAu5QSBTQbToLUu4wCYy/BNoinqFkXiTZEaV6/5PJIeNsz75fn
AEuBpIATEHqRsY6L9HdYAvBiAEv6ufCZhzwWF7ph1ZMhg6ulfoaQwoVCkbKi/zgw
i3tvteda5vXQs4e8GvgZ6zkabQ4cZFjVpb3dA6huBbs20jLfYYXsXWsJEGF3JK5o
kQ08+u/h/q0lFDFa70S9hpQXMtSfCCW/AuEc/+tG7rnUul1oMXjpVnDOmg+CZfIY
gi9D30/zd1DYFJOEwawl5FKLFQY7TOn3RlZ3SR4mNbbhIgHPL9S1xHGcm8UC7PKu
Ogh8+5Nl0aeeUB1liuzM/w5JbF4L4FoZW6ciNQIDAQABAoIBAAEnd9Xq3GknAm6V
/bTFCDQQ8rElPTEVsaWRVO5wdDQ0KxVkEqKMlGNh+1wMWQWl6iQKsPKxrnSwgv4u
Zg9wNfWW6r+w7FGeVoIZC157Ce4SEpEt9BSDoawVnJhTtmFIakajNKufGhPGNLQE
XOosaSX63RRxcrSn5fp4Y7wuaqucXzPV3MuRABrDTHugW1O37570HLwvY9Hmf2+N
WIqEasCjeZ575EwTcqqVwB/j3BNqyrbkxP6aFszxYdMvlK/mpPhrShV6+QmuP02j
OvRt9HM8knUVzsGJzuDk5V5VUGejih3VZO1BuqP1LFb1j4siWBoKpVQrP0wSJzaa
5Vru/wECgYEA9SygrrkPESbjSLYIJU4mRmC4zSqbUoWpvTgpK29nNmif7BSJ9bRr
2QSCIho58a0bl3a7GLwY0dnEyDYKF9nTQ8HqlkvwRwxdMql+0hU0o5hOBnljYoyM
BBptFvoihCe2se+1FEQkunAFCDph00S5utz+8uTlHSFiSy/fQ4GOZwECgYEAw9xl
kZOPSGNEd69F9gtkOAT0kWuazvePSoeYNldZ/MZyE0zE4Wyh5Dk8y5cBt3Pw0ZTr
NN1dc24EkjcFIBop4Me5n5zKr4rH1XKsUQ2jaTurp/fVQwGo820n+ChO8cIEobHY
p6wmKopnnDKOATMjZd8hg+Gck7qWoeTxk3XizzUCgYAFGGJWfz4S6y36Ct5seA1P
lR8CFIqZ0nFOn2YrousQNGhubZbYZmF/ZxqVPtpJbYGPSkZlIzOY2N/AEW9wQ3Si
idsoOHfL4jPlo6QhFZO8eqPUep1YJPeb9jiiK5ygBntDg2nN/ASPY1iXbS8vRtRd
T850mdExI8p5KYuISZ7+AQKBgHF/ENhoErqW04ErbzYh6cRQksyF92KBsGY25uxu
d/XzpP0sGlaq1bFjvagYbGU7aUx5qEatFE8kbL+x5GVy49uewSEOAaHxoNU+qz4Y
0h3T9yfRhKJcnuPY2DWEXiLYFEkCvxKCvmceZuXrocBuOs/4mfpLTamJkWplOdwC
jxkVAoGBALU/996M9frxtjvzQdF3ncbKrDcptZN6s1hIBVk1t2PzcT0XgiOUZRqj
jvUqZWg7MV6kovdjg7N2KSE0u9p2nysL4ejvZRKMNNBdaFJvOUcaYCMTHok1KfXp
Bq75mMgfFLrY+huWzKdRWkEjqD6A05Us48iuikW5Ec32sI6mUzg9
-----END RSA PRIVATE KEY-----

View File

@ -1,61 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFrDCCBJSgAwIBAgIQDw9E1qRxoRsSagKtIUbGJzANBgkqhkiG9w0BAQsFADBy
MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywg
SW5jLjEdMBsGA1UECxMURG9tYWluIFZhbGlkYXRlZCBTU0wxHTAbBgNVBAMTFFRy
dXN0QXNpYSBUTFMgUlNBIENBMB4XDTIxMDMyMDAwMDAwMFoXDTIyMDMxOTIzNTk1
OVowKTEnMCUGA1UEAxMeZGFpbHkuYXBpLmRqYW5nby12dWUtYWRtaW4uY29tMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyijP5M9nLFbIimHuhYl4cK7C
e4SewgiERRThx+4/iLzFKMVHpSQ0GgcSN7e6tLlfgMzRcUbAnUsia4bzcp0UG+w0
Ny6OrFF7XuU71YQ3B+35NpfAvJGr7pw71aFpl1l+jz9iym4iw6yN5F8poq6TR7Gl
PNhX1YWZ+Wy0MCDnvQ00MBsQ/42CGEPzE6ZdKJ3l19NY/9/djnxZE+OgMVR3cdCI
I/Et0mahIWYaPGP14/nIzNfOcEZtJMHmgSGF/l9NyUdsrRRK7ltV87YWeU86e2Da
hqW9v0oSOLepOzg4PB4wU2IJfykRMXfJNUG6iWjOMVPdqIFTXjJZvtvZg5J4GQID
AQABo4IChTCCAoEwHwYDVR0jBBgwFoAUf9OZ86BHDjEAVlYijrfMnt3KAYowHQYD
VR0OBBYEFOqwsoYN4FxXPwL2my2kdh/PuVB4MCkGA1UdEQQiMCCCHmRhaWx5LmFw
aS5kamFuZ28tdnVlLWFkbWluLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMD4GA1UdIAQ3MDUwMwYGZ4EMAQIBMCkwJwYI
KwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBkgYIKwYBBQUH
AQEEgYUwgYIwNAYIKwYBBQUHMAGGKGh0dHA6Ly9zdGF0dXNlLmRpZ2l0YWxjZXJ0
dmFsaWRhdGlvbi5jb20wSgYIKwYBBQUHMAKGPmh0dHA6Ly9jYWNlcnRzLmRpZ2l0
YWxjZXJ0dmFsaWRhdGlvbi5jb20vVHJ1c3RBc2lhVExTUlNBQ0EuY3J0MAkGA1Ud
EwQCMAAwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdQBGpVXrdfqRIDC1oolp9PN9
ESxBdL79SbiFq/L8cP5tRwAAAXhNdRxuAAAEAwBGMEQCICntYjFg4csRL4bxiJvr
ma95JmMzYRrLx84UgLbd1iWhAiB64xH69vjCf4JfVB+3G1UN6G4pphW4m/nYBA2E
ElFuIgB2ACJFRQdZVSRWlj+hL/H3bYbgIyZjrcBLf13Gg1xu4g8CAAABeE11HF4A
AAQDAEcwRQIhANsjDO2cASF5sGZCyjS8jz9qA+5N4WWFxG+tO4VyELjtAiAIqurr
QbHF9fkBFDg995q5eR16ncpS1d75a0Nhis62PjANBgkqhkiG9w0BAQsFAAOCAQEA
DADo2zekDxm3zbuHGAlQR17h4BtD3tRDy58J58L5CnL+VhNLAN2Fp4AQF1w4a4+z
89ZFOKn+RcwSwyYDFHqsKYa7QYL97z7GJ1ASPG79faO5osbo1SOrSdwzIjjUnSD5
V4Fr0Fw6NYkq72pJPZkPEeivGEG3MvhzTaFSTwkylDVx6wxzaPY/p9WJpbopKN/8
+2WSM0b6UB1cuOgfJlhcqYYFqAPO/2XLY60uPpKgboPFAznT8T2QSaYd7JXHndYJ
IgTtXPr7hPOeXP9kDdUycCf3WOjp00gSk/guSujW4ZEaqCrJOkiMCbOrgyCIF7AK
pzwp4H5aJpzHXqObsb5n3Q==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIErjCCA5agAwIBAgIQBYAmfwbylVM0jhwYWl7uLjANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzEyMDgxMjI4MjZaFw0yNzEyMDgxMjI4MjZaMHIxCzAJBgNVBAYTAkNO
MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMR0wGwYDVQQL
ExREb21haW4gVmFsaWRhdGVkIFNTTDEdMBsGA1UEAxMUVHJ1c3RBc2lhIFRMUyBS
U0EgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgWa9X+ph+wAm8
Yh1Fk1MjKbQ5QwBOOKVaZR/OfCh+F6f93u7vZHGcUU/lvVGgUQnbzJhR1UV2epJa
e+m7cxnXIKdD0/VS9btAgwJszGFvwoqXeaCqFoP71wPmXjjUwLT70+qvX4hdyYfO
JcjeTz5QKtg8zQwxaK9x4JT9CoOmoVdVhEBAiD3DwR5fFgOHDwwGxdJWVBvktnoA
zjdTLXDdbSVC5jZ0u8oq9BiTDv7jAlsB5F8aZgvSZDOQeFrwaOTbKWSEInEhnchK
ZTD1dz6aBlk1xGEI5PZWAnVAba/ofH33ktymaTDsE6xRDnW97pDkimCRak6CEbfe
3dXw6OV5AgMBAAGjggFPMIIBSzAdBgNVHQ4EFgQUf9OZ86BHDjEAVlYijrfMnt3K
AYowHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQD
AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG
AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
ZGlnaWNlcnQuY29tMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwTAYDVR0gBEUwQzA3Bglg
hkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29t
L0NQUzAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAK3dVOj5dlv4MzK2i233
lDYvyJ3slFY2X2HKTYGte8nbK6i5/fsDImMYihAkp6VaNY/en8WZ5qcrQPVLuJrJ
DSXT04NnMeZOQDUoj/NHAmdfCBB/h1bZ5OGK6Sf1h5Yx/5wR4f3TUoPgGlnU7EuP
ISLNdMRiDrXntcImDAiRvkh5GJuH4YCVE6XEntqaNIgGkRwxKSgnU3Id3iuFbW9F
UQ9Qqtb1GX91AJ7i4153TikGgYCdwYkBURD8gSVe8OAco6IfZOYt/TEwii1Ivi1C
qnuUlWpsF1LdQNIdfbW3TSe0BhQa7ifbVIfvPWHYOu3rkg1ZeMo6XRU9B4n5VyJY
RmE=
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAyijP5M9nLFbIimHuhYl4cK7Ce4SewgiERRThx+4/iLzFKMVH
pSQ0GgcSN7e6tLlfgMzRcUbAnUsia4bzcp0UG+w0Ny6OrFF7XuU71YQ3B+35NpfA
vJGr7pw71aFpl1l+jz9iym4iw6yN5F8poq6TR7GlPNhX1YWZ+Wy0MCDnvQ00MBsQ
/42CGEPzE6ZdKJ3l19NY/9/djnxZE+OgMVR3cdCII/Et0mahIWYaPGP14/nIzNfO
cEZtJMHmgSGF/l9NyUdsrRRK7ltV87YWeU86e2DahqW9v0oSOLepOzg4PB4wU2IJ
fykRMXfJNUG6iWjOMVPdqIFTXjJZvtvZg5J4GQIDAQABAoIBAFYjOqXXe7IoTi2s
mbnbf+6fgC2qLg0mHNnkkmmiif7E1EtRd/wVJ4AZmDkWd57ux5M1cl6OU58R9xoS
9+NTq9BT/lGu7ErfMy6VhT+upNYjn4cT9SND/Jrghhw6OSgskWEPFJSFhhmTCiiP
Jcn0EbxAJNR+qDpKQXfGSiahtqxV01kNRU/j6YmVrQEoVif/9+LJmcs5Uy+gqA4a
fQou8/QQjrmPHPOSSWZ8aZlPNt89+E4P+XIkf4rPxD8lrWd2ufDeCdm1As4qYQQD
c/eeBh6qkrKijZZ8NRgonTaCYPXkkkHME4BgdP2gVCW/KMVfdM5pVVk7dctSs+6w
VFfF5CsCgYEA/pXLQXt6PUlJAVSFodK8CndZY1pAbzLNVTsDAVuEAepwmEL7Tw0O
nV4igsANdRxZxEdYWsY8CClNTNLNY92zaAbpsQXHxHqnXnl2ZGQi1Jb2eFR6zw+m
8Qyb6aca58Q95flHQY75/+IFWemj76p1aT7aIvpElMdkBUh1Ak92cv8CgYEAy0hu
MqJga0/3UoMLhjhUVWGXI6NBpvWNv7FqSeQ2SDj0iBib7nyrlP41bCcyHFXryrXf
YsbG4fq0mmyhQUz4++OP57lxXXBu3UvBd6qQUorWX9HVHy6ljun8PsJdNUGj9Q2v
arH6Rv/emIx86IAOGjgIgK+YiNwiw/uEYgNzTOcCgYBryjh4zTMAZ9sFOSgrT/JV
7Bpounm1myjdAVNQa9MEjKKHlTSaT8j0UDsEaRRJlWtcc2ixZmVcf0A/WrGjquaf
EO45CV1/jv72PS8nak5k/FX2tK4apWHlhZUt5Ja7spcSm+zTkRnAgY4Kd6X1f5Ke
sQHi9Vu8Mn/izL7d748TOQKBgH75txuZoXBmeq3nfQNRnBvY4Xc5OoD3UJs0Tpfp
HJ7wNI3uETheVy6xutzbfsmEQcxU3jvsvb3Zw4XR5MfNNJjiA7lSdCVRXW6NK0N8
HrnwTwd7IgxgLrmeHhl1fpMNdURUUAXtNc+zc28GEd+IXUazSVxYUobqOi0Apigy
z4pxAoGBAPKd+WMUS94Ne7WMHDzcCcQKl2dJmBVj036qCtPgUL4yDyRDc/H1fQxI
jr/+JueqktrJgJsOL4uygn0zzKIJyQIToufxOodUdmviRLaH2UskNUBUjIBqyDc0
pjPebC7sxg6HZQmVpgX4klj1IWDpudD+cCRGF9SIzKRvNEadLgOl
-----END RSA PRIVATE KEY-----

View File

@ -1,61 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFsDCCBJigAwIBAgIQBbQIKLDxaNeKW6HjUJCFNjANBgkqhkiG9w0BAQsFADBy
MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywg
SW5jLjEdMBsGA1UECxMURG9tYWluIFZhbGlkYXRlZCBTU0wxHTAbBgNVBAMTFFRy
dXN0QXNpYSBUTFMgUlNBIENBMB4XDTIxMDMyMDAwMDAwMFoXDTIyMDMxOTIzNTk1
OVowKjEoMCYGA1UEAxMfZGFpbHkuZGVtby5kamFuZ28tdnVlLWFkbWluLmNvbTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKAOwxsoKm4vH/gv8h7OHYHI
5dQAAKm3tk1ZsyY9cMlSq/GIMSY15BZSwdSiwyI3vcCpmz2YCVRXpH8r0LNm/Ygu
fjVeK1H+F6fQkGj7065ye1Z15PtgI6Dx9dhLlFlR3lZYm0MVwSIW2xgVqbL+WNKl
198afnP+/B/Klzjyud4wqAlzDcD8lIh8iL0LMY3HSHzSzr8xtYZLxUvEzcl+vRF9
a9GjTOgg01Ukk04zHXan7VInK1/lurQ+WuntfPFDe/jmlEid4/wvB/lCMi0KUSWd
0C9CCB+2q/Lsa0hr8v4DF1TS1/YGg/y0Ye1+DzpKLKv/mfe6py18Ca7FcnFoKVsC
AwEAAaOCAogwggKEMB8GA1UdIwQYMBaAFH/TmfOgRw4xAFZWIo63zJ7dygGKMB0G
A1UdDgQWBBQ14z4HfGEjC4tAPVnN/AvsXmgHYDAqBgNVHREEIzAhgh9kYWlseS5k
ZW1vLmRqYW5nby12dWUtYWRtaW4uY29tMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE
FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwPgYDVR0gBDcwNTAzBgZngQwBAgEwKTAn
BggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGSBggrBgEF
BQcBAQSBhTCBgjA0BggrBgEFBQcwAYYoaHR0cDovL3N0YXR1c2UuZGlnaXRhbGNl
cnR2YWxpZGF0aW9uLmNvbTBKBggrBgEFBQcwAoY+aHR0cDovL2NhY2VydHMuZGln
aXRhbGNlcnR2YWxpZGF0aW9uLmNvbS9UcnVzdEFzaWFUTFNSU0FDQS5jcnQwCQYD
VR0TBAIwADCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB3AEalVet1+pEgMLWiiWn0
830RLEF0vv1JuIWr8vxw/m1HAAABeE1yhJEAAAQDAEgwRgIhAIdZiX5qq+KJjdtl
2Z9ejh5o9VHKyVVUgkJb0+S8aQGlAiEA5nEVffySiTcUogz+b7n34hmG2aW3YO02
UhMzyv7hoGcAdgAiRUUHWVUkVpY/oS/x922G4CMmY63AS39dxoNcbuIPAgAAAXhN
coRmAAAEAwBHMEUCIDFWIi3mkGmkB/uuTCqJyPTz8/WS4vqvdAUWO+MuxuaoAiEA
yP80fJCLnfso7VoJcWQT1l6bN6XL/jxip1OjXU0+dQMwDQYJKoZIhvcNAQELBQAD
ggEBAB0vH0OMrwGOQHdRdaN9t+2Yc1xnBNcksHcCmVD09gO2clA06OqTiu77g8lw
KwO2M5DmNFAA2onDxRVIL6auyLNdyK4X2m9i1eGnyAKro7d4owIlG30JdJ7uHzLG
ROL9SR0ibNZwOhoKSfpy3a+fWngMjCdW8pAFJHfPbOf6lP/CpGwoBnLvm9a9ZG3K
m2NmFN1/GPje25tXy0S/NaVFDVcJyfMeuVKqdwY9sbN7nOYGUury5NC5LWbgJ0T2
KWLcZc3LCa791kHLWVt/KI+9UgpM6Xiie0jUUIU92NaxBD/wY/jybPiogJkAz/sl
TncbnwO2+WpN6jIy9ccs8ogUMTQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIErjCCA5agAwIBAgIQBYAmfwbylVM0jhwYWl7uLjANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzEyMDgxMjI4MjZaFw0yNzEyMDgxMjI4MjZaMHIxCzAJBgNVBAYTAkNO
MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMR0wGwYDVQQL
ExREb21haW4gVmFsaWRhdGVkIFNTTDEdMBsGA1UEAxMUVHJ1c3RBc2lhIFRMUyBS
U0EgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgWa9X+ph+wAm8
Yh1Fk1MjKbQ5QwBOOKVaZR/OfCh+F6f93u7vZHGcUU/lvVGgUQnbzJhR1UV2epJa
e+m7cxnXIKdD0/VS9btAgwJszGFvwoqXeaCqFoP71wPmXjjUwLT70+qvX4hdyYfO
JcjeTz5QKtg8zQwxaK9x4JT9CoOmoVdVhEBAiD3DwR5fFgOHDwwGxdJWVBvktnoA
zjdTLXDdbSVC5jZ0u8oq9BiTDv7jAlsB5F8aZgvSZDOQeFrwaOTbKWSEInEhnchK
ZTD1dz6aBlk1xGEI5PZWAnVAba/ofH33ktymaTDsE6xRDnW97pDkimCRak6CEbfe
3dXw6OV5AgMBAAGjggFPMIIBSzAdBgNVHQ4EFgQUf9OZ86BHDjEAVlYijrfMnt3K
AYowHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQD
AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG
AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
ZGlnaWNlcnQuY29tMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwTAYDVR0gBEUwQzA3Bglg
hkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29t
L0NQUzAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAK3dVOj5dlv4MzK2i233
lDYvyJ3slFY2X2HKTYGte8nbK6i5/fsDImMYihAkp6VaNY/en8WZ5qcrQPVLuJrJ
DSXT04NnMeZOQDUoj/NHAmdfCBB/h1bZ5OGK6Sf1h5Yx/5wR4f3TUoPgGlnU7EuP
ISLNdMRiDrXntcImDAiRvkh5GJuH4YCVE6XEntqaNIgGkRwxKSgnU3Id3iuFbW9F
UQ9Qqtb1GX91AJ7i4153TikGgYCdwYkBURD8gSVe8OAco6IfZOYt/TEwii1Ivi1C
qnuUlWpsF1LdQNIdfbW3TSe0BhQa7ifbVIfvPWHYOu3rkg1ZeMo6XRU9B4n5VyJY
RmE=
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAoA7DGygqbi8f+C/yHs4dgcjl1AAAqbe2TVmzJj1wyVKr8Ygx
JjXkFlLB1KLDIje9wKmbPZgJVFekfyvQs2b9iC5+NV4rUf4Xp9CQaPvTrnJ7VnXk
+2AjoPH12EuUWVHeVlibQxXBIhbbGBWpsv5Y0qXX3xp+c/78H8qXOPK53jCoCXMN
wPyUiHyIvQsxjcdIfNLOvzG1hkvFS8TNyX69EX1r0aNM6CDTVSSTTjMddqftUicr
X+W6tD5a6e188UN7+OaUSJ3j/C8H+UIyLQpRJZ3QL0IIH7ar8uxrSGvy/gMXVNLX
9gaD/LRh7X4POkosq/+Z97qnLXwJrsVycWgpWwIDAQABAoIBABaP2NHdrhQnwWu/
T8km3dhFz5vm6jECVrN9nE1yJhFtqBOH0qx5C+9Xor9iDXv9JoWKLaTCPI0ZPkoM
4kI/wietrMMXb1IuAqX4YOWORgRrr9YmupdIBzhbPdVLsMbhzaPB9r+UnqEQXrmn
ksB1WQ5MgWsPdYQit+XqrWQXgbrU9kAP5+aO2GKrlnWZqBgshUkd2Sq5rdgrK5nv
kcmVBGClPsgOG2HgA+aJoFGLccoF8NMrBWaSc+zlHjiT8fP2YnMcDsX8Fw1tETbK
P9jf9wd4b/2ezKL3TWJMkbTRnxBwDgARWFVrD9jZqLtw342YuGQnEhPJFFdepKpQ
3GtbyiECgYEA0KlSF3K+coEjCjBGSFD5ACmB5ipl/NFPXVjCG1vHGHRh/0WnXBPI
zY3pRHFr9cJKLxQPe+4085yFXGTV7GHhH4nd1tEVxrH8ouNok0/WL4HHtvfGOq/H
sKQsZKVaUXd+NTZmzoXXJsb9Jgnu6r4AS4QazP1FRxSbSToxbut1ezsCgYEAxF6f
mAFz9p5ptNStJLXV53nTgNHK0Hk2cISu5aAUGQKA26VwnZTEo09Wt+WOGjzjTkSi
+TCoFfG77aqFQ1AyuAYWWErcS4/1/p0SeINXdztx34CJM/OqJ/AtjdL+09xwUZe6
JeUWWPMJukkrFkjNkCjkdQlVxG+a3jMlmM016GECgYEAoJJ8QuEhH7qyvUdy/nmZ
dH98oCPmggyM15fTH/yblP0S4L+4T8pFz7EyXmIuI1xVfC8iz0r7YrEDi5tpaFPW
S0/r8EDMUjBr6um3cw2QFNT5XJsF5+mXcR6Vrwn0HBcpf0eTC8DBVezxqFEik3CN
h49slG0e39lCurJWkjYOHTsCgYBeA/Oi4ic0DvoLErvm1IwZ7BDgHxFcKHxw+IWH
+NFGfBVXk+jL+Vr/2U9qciRL2ZT2dxQT/ECtaPQRwM9WwAHYa0mtcgHwx3b+NROP
0UpCEprdZ/vIfMOdpXcZ7MgGhQbdeags1naRlaK1pqxTWf3ZJErk4dhHWSurcI9y
jeVeYQKBgQCsIaGmX/xigoLaEucXOM4V34QU1gepJkDLDNEw4r9EoNHoBCwIaGxX
CWCsyuOwnCWM2OJJth5a+1xPYul0jsh7HCShICpkMsktoq4UdYbBfVNQiqenI7L7
l6/mzU5Ds6KXKVh2FZ7O8zcup1BYs0pAHTZqF+P/1FjSu1jbGfezFg==
-----END RSA PRIVATE KEY-----

View File

@ -1,61 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFoTCCBImgAwIBAgIQBL1afhbyouKA0yBDgDhq/zANBgkqhkiG9w0BAQsFADBy
MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywg
SW5jLjEdMBsGA1UECxMURG9tYWluIFZhbGlkYXRlZCBTU0wxHTAbBgNVBAMTFFRy
dXN0QXNpYSBUTFMgUlNBIENBMB4XDTIxMDMyMDAwMDAwMFoXDTIyMDMxOTIzNTk1
OVowJDEiMCAGA1UEAxMZZGVtby5kamFuZ28tdnVlLWFkbWluLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMk2tqLVSJyC/VTuWlZ+a+bjthUotJ/o
yQcXMC6eu/KyHbWqh5Ccj2kLh/zf9uhcs/GcYl/H1hyIVuiC+SHGmGfCqQB96vLl
uwwJDlM+y910xKmKV2S6DEFbXDZP76pShrpPs1nDkU5gftuGhX1S77pmMqjbOsG+
gZJAlAZKzT5xnGwpo4rkHc8xPY0ac29+9HXYDawpmySSdbfG+W0kape/wBexFmO+
KLt/oRk+QJbtPguLNLLlvZznf+fFp5UcmsVks/scg9fM1pnj3QQaCB8RXQQKkskm
Hi67PGFJOeepNFkApzHO4sUM3ya2Rskzfs9DtTlCODn3XFSVJz7DyTsCAwEAAaOC
An8wggJ7MB8GA1UdIwQYMBaAFH/TmfOgRw4xAFZWIo63zJ7dygGKMB0GA1UdDgQW
BBSRBeJj2dUcPqhi3km5tbW5lZP3aTAkBgNVHREEHTAbghlkZW1vLmRqYW5nby12
dWUtYWRtaW4uY29tMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwPgYDVR0gBDcwNTAzBgZngQwBAgEwKTAnBggrBgEFBQcCARYb
aHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGSBggrBgEFBQcBAQSBhTCBgjA0
BggrBgEFBQcwAYYoaHR0cDovL3N0YXR1c2UuZGlnaXRhbGNlcnR2YWxpZGF0aW9u
LmNvbTBKBggrBgEFBQcwAoY+aHR0cDovL2NhY2VydHMuZGlnaXRhbGNlcnR2YWxp
ZGF0aW9uLmNvbS9UcnVzdEFzaWFUTFNSU0FDQS5jcnQwCQYDVR0TBAIwADCCAQIG
CisGAQQB1nkCBAIEgfMEgfAA7gB1AEalVet1+pEgMLWiiWn0830RLEF0vv1JuIWr
8vxw/m1HAAABeE1whV0AAAQDAEYwRAIgPFX8oV29XVZMDi2tbEuBo2AuS4udT2I/
tMHo9EPNPOYCIGFeOpkGfPcG4szSvKEmUIYimb3vejH0QJ6qB3+T6E5kAHUAIkVF
B1lVJFaWP6Ev8fdthuAjJmOtwEt/XcaDXG7iDwIAAAF4TXCFeAAABAMARjBEAiBC
HxlN8ItLz/FW/gYrmGGXTVvxxnVKv9t5bsj2AsdmOQIgJVu8zYZVNjt7AHFusZWg
ZYSY0Qd+11v1oXBHAi0OUAowDQYJKoZIhvcNAQELBQADggEBAEKj+pGihWMbm/Bm
d+7Ra6gngG5N/c7S1cPzmOAlX3bWOFLqZRVPiC8Xa5uzLuqkYMiRIuIFUjZj2CwS
gKVwEk3xIL76sMVURV17mhjV7jbhzCSIqnEVLpiGRsuEoHKW8UUAzBdAZjjk4D+Y
LZKOQKcWojGTCyRJA5dRHy+sWtnZcHGaD36UHeVh0ctFhoVcc9Oxsh9T6W4NnMd/
CthO8fDic3643232J+6VF1vH5fkz1Q6UGbH+GbmkZ70Rv9YCl78zU77juBfTy9ht
HJmcwLWF6Hef7Z+K2QnvT6H+cIb6OxSErshy9TlKgTRjVD7A3wTrri/dCJqfINVk
FoBVzmQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIErjCCA5agAwIBAgIQBYAmfwbylVM0jhwYWl7uLjANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzEyMDgxMjI4MjZaFw0yNzEyMDgxMjI4MjZaMHIxCzAJBgNVBAYTAkNO
MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMR0wGwYDVQQL
ExREb21haW4gVmFsaWRhdGVkIFNTTDEdMBsGA1UEAxMUVHJ1c3RBc2lhIFRMUyBS
U0EgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgWa9X+ph+wAm8
Yh1Fk1MjKbQ5QwBOOKVaZR/OfCh+F6f93u7vZHGcUU/lvVGgUQnbzJhR1UV2epJa
e+m7cxnXIKdD0/VS9btAgwJszGFvwoqXeaCqFoP71wPmXjjUwLT70+qvX4hdyYfO
JcjeTz5QKtg8zQwxaK9x4JT9CoOmoVdVhEBAiD3DwR5fFgOHDwwGxdJWVBvktnoA
zjdTLXDdbSVC5jZ0u8oq9BiTDv7jAlsB5F8aZgvSZDOQeFrwaOTbKWSEInEhnchK
ZTD1dz6aBlk1xGEI5PZWAnVAba/ofH33ktymaTDsE6xRDnW97pDkimCRak6CEbfe
3dXw6OV5AgMBAAGjggFPMIIBSzAdBgNVHQ4EFgQUf9OZ86BHDjEAVlYijrfMnt3K
AYowHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQD
AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG
AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
ZGlnaWNlcnQuY29tMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwTAYDVR0gBEUwQzA3Bglg
hkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29t
L0NQUzAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAK3dVOj5dlv4MzK2i233
lDYvyJ3slFY2X2HKTYGte8nbK6i5/fsDImMYihAkp6VaNY/en8WZ5qcrQPVLuJrJ
DSXT04NnMeZOQDUoj/NHAmdfCBB/h1bZ5OGK6Sf1h5Yx/5wR4f3TUoPgGlnU7EuP
ISLNdMRiDrXntcImDAiRvkh5GJuH4YCVE6XEntqaNIgGkRwxKSgnU3Id3iuFbW9F
UQ9Qqtb1GX91AJ7i4153TikGgYCdwYkBURD8gSVe8OAco6IfZOYt/TEwii1Ivi1C
qnuUlWpsF1LdQNIdfbW3TSe0BhQa7ifbVIfvPWHYOu3rkg1ZeMo6XRU9B4n5VyJY
RmE=
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAyTa2otVInIL9VO5aVn5r5uO2FSi0n+jJBxcwLp678rIdtaqH
kJyPaQuH/N/26Fyz8ZxiX8fWHIhW6IL5IcaYZ8KpAH3q8uW7DAkOUz7L3XTEqYpX
ZLoMQVtcNk/vqlKGuk+zWcORTmB+24aFfVLvumYyqNs6wb6BkkCUBkrNPnGcbCmj
iuQdzzE9jRpzb370ddgNrCmbJJJ1t8b5bSRql7/AF7EWY74ou3+hGT5Alu0+C4s0
suW9nOd/58WnlRyaxWSz+xyD18zWmePdBBoIHxFdBAqSySYeLrs8YUk556k0WQCn
Mc7ixQzfJrZGyTN+z0O1OUI4OfdcVJUnPsPJOwIDAQABAoIBAA8baQX7vnplyVBt
nuG8lyxcL2kSR9FzwFgkcQ0nBNR5dAqWNZxxbMEFyR1+0UJr52S+CZLIZbZ5tBC7
+KmFCB9OObMcQR4ginUiXu14GwVTBYr3JI2e/FmR2vAG+2cN0Ci/4CberJO2Yf/o
bzBUIESd9LLB1v0B6SeKarK4PgWwjrr/twowABRBhkkKFErfYZjfNh1mALXPNEUA
7z3a8abw+JWympIFKWxi3YXyLOJZaGawOPDhq+ZVv+7rXvIyAzUzpmwCprjZFL4Q
hgN7IY7KjYK772Ffe0nwMbd3wbwYeAelM9fndH9dTMrQ1n6EcDBzla8Cwl6OVEk3
GpQqOfECgYEA+7qFHw75AOI3FFke/2VOJ58m0rrYJwDrLazlrzashi7qgJOBY6jp
yjLgGCOcR9v8pRWkOw2VLy+dhdr16Xp/XYhuSa6O5EC7igtnUVx9xu+XAksZiB97
f4lm7F3uirEh7ewuqiRETI9o6V5iGTCSMSc21zlJ1zoxnLMSuMyCKTECgYEAzKDD
PNJVZAiZJ25JKinkZlTyQ9VmQkfrvsIBWheN844uoSmmLR4PNwriZQtlURI58WA2
pe20aHJaleOJvzgW/OvcB+7mf/Ukn1mw6Ciusj6jXLXmmq8i80W0RMr1Rdbq8+dp
DNZt0iDXEZXHEGqdkpkn/gsvYL+jeIiKyuJ9PisCgYAoZGF//lMORT45UaObr5G+
4dbE8Z5Fg+w4xAmG9+rvDRAr2X9lknERNOCofu5QyYfcpYBYyXEqxSUtmVjkQfe1
9nJb+FqNXaW6HOJTN9gm18MPZyWNph+W82FEhD4Gmy2qk79ZJcCf2FMpPy/Wguiy
Ymx2VIb4tinHzyQt6wLnwQKBgAURSiR8dP7oM5rFYWx44x4hpmpFo6WqkE0GEvB/
OtW4RLFbDbF6WBgd3eNwt86dK/AtWM0dKOWZR2ME4olowzD6SlWr9etfT8vedcIa
F9F0Oal3G8Hi6nOp20AE4rQbEXB+35wgx1F33LujwO1IJqTVxCbHciHsPQkkIIPL
vhxHAoGAdwbRiK2OCmycedsD+L5qdxOYiL8JIAXLP+H7ZD0Jyx21UxQAmvza6fIx
HygnDGzrTJoOPEWu5WmGs4i6mr1bChHpo85LVNfytsxbE59y3I0va1IlSUC6wjXP
Yy3caUiBgxmzz4vhqCFdpLhT2SyyB4m13ggLmeyqLy88WnYCgiE=
-----END RSA PRIVATE KEY-----

View File

@ -1,61 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFszCCBJugAwIBAgIQCGwikUrRfEUk8GbxRWLVIDANBgkqhkiG9w0BAQsFADBy
MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywg
SW5jLjEdMBsGA1UECxMURG9tYWluIFZhbGlkYXRlZCBTU0wxHTAbBgNVBAMTFFRy
dXN0QXNpYSBUTFMgUlNBIENBMB4XDTIxMDMwMzAwMDAwMFoXDTIyMDMwMjIzNTk1
OVowHzEdMBsGA1UEAxMUZGphbmdvLXZ1ZS1hZG1pbi5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC0iitqUeTxkq5qabbVcB+7a+YArfKAJBRvcj+T
VVdRkvEjcfGLvXIJUTvcUqJkRRYFbimdjB5q7CWCld8bgz/UOpIEmbdqn+WgzrNn
WWZrRiN5ylHAsIaKlgYKh6gcftEco6OgkrM8oyq1gORRBIcSj95VcqpIeAUMVyCX
R0h3dXnLCVe70ejr8w2cyHrSeS68mXw7LlAdfC0JAZTB/wxwcitSnnIVEbV6wMzG
BD5Ka/WprnJgJa7Prb5KHiZh4UG4Es2JpIHALMYLIeKQCUCIP1NmW8Ale+nhY9kD
tJeSEIY+/dRoDjpPkkIZalMp+5p0yDQ1LIO4pDZxnoI2vFLFAgMBAAGjggKWMIIC
kjAfBgNVHSMEGDAWgBR/05nzoEcOMQBWViKOt8ye3coBijAdBgNVHQ4EFgQUru5P
/0LuJj6hxqHWVYUN690wFNEwOQYDVR0RBDIwMIIUZGphbmdvLXZ1ZS1hZG1pbi5j
b22CGHd3dy5kamFuZ28tdnVlLWFkbWluLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYD
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMD4GA1UdIAQ3MDUwMwYGZ4EMAQIB
MCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBkgYI
KwYBBQUHAQEEgYUwgYIwNAYIKwYBBQUHMAGGKGh0dHA6Ly9zdGF0dXNlLmRpZ2l0
YWxjZXJ0dmFsaWRhdGlvbi5jb20wSgYIKwYBBQUHMAKGPmh0dHA6Ly9jYWNlcnRz
LmRpZ2l0YWxjZXJ0dmFsaWRhdGlvbi5jb20vVHJ1c3RBc2lhVExTUlNBQ0EuY3J0
MAkGA1UdEwQCMAAwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdgBGpVXrdfqRIDC1
oolp9PN9ESxBdL79SbiFq/L8cP5tRwAAAXf475pDAAAEAwBHMEUCIHTgL1+oRWE+
REuWNRa5J1cflPKW63jk6/3ibF+Sm5peAiEAt74SK9Ka9QyMJfP57eDl3xA1Ctnb
Bz6zTLIQFFicPGsAdgAiRUUHWVUkVpY/oS/x922G4CMmY63AS39dxoNcbuIPAgAA
AXf475pNAAAEAwBHMEUCIQDOE36oL7MAxmHdEoI338jLW0xnBN8VeW9XyuTzIcq7
eQIgLwJTNfLqgw7TofY3dKqof5rkcuJWMmSI4aapUUBjFfIwDQYJKoZIhvcNAQEL
BQADggEBAC9lYBZuCzle1tSaJ4nVfogwHf0B4lKjDI8ixEnrDaY0tAuYst4S7zoU
76dZcpdwQjVLtlLk5jyaNdN5ohsu9WpErq6N7bXKhbSPBavCPxP4sMWMvmeQQUtB
UKqqRofXbx3WcWtsf1/Wlg3pyb0oyJ7kKt3iDG9OYEihQzEyG5Y72IE+HsbhTAWR
zqxGnmMumQ+JTnxCCudPoVPk25i2BfndMbcY4N6YyJYuLwMojtMkAeWtRdMmqtiy
lMb7EbWLYCHDyv892WTzie1BqZ7YcOaE59OChhmoYxiSXNHPCToIFECTz6BhPxlI
T3LbIM4+81HNDebdR17NUE3zN44OjsA=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIErjCCA5agAwIBAgIQBYAmfwbylVM0jhwYWl7uLjANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzEyMDgxMjI4MjZaFw0yNzEyMDgxMjI4MjZaMHIxCzAJBgNVBAYTAkNO
MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMR0wGwYDVQQL
ExREb21haW4gVmFsaWRhdGVkIFNTTDEdMBsGA1UEAxMUVHJ1c3RBc2lhIFRMUyBS
U0EgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgWa9X+ph+wAm8
Yh1Fk1MjKbQ5QwBOOKVaZR/OfCh+F6f93u7vZHGcUU/lvVGgUQnbzJhR1UV2epJa
e+m7cxnXIKdD0/VS9btAgwJszGFvwoqXeaCqFoP71wPmXjjUwLT70+qvX4hdyYfO
JcjeTz5QKtg8zQwxaK9x4JT9CoOmoVdVhEBAiD3DwR5fFgOHDwwGxdJWVBvktnoA
zjdTLXDdbSVC5jZ0u8oq9BiTDv7jAlsB5F8aZgvSZDOQeFrwaOTbKWSEInEhnchK
ZTD1dz6aBlk1xGEI5PZWAnVAba/ofH33ktymaTDsE6xRDnW97pDkimCRak6CEbfe
3dXw6OV5AgMBAAGjggFPMIIBSzAdBgNVHQ4EFgQUf9OZ86BHDjEAVlYijrfMnt3K
AYowHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQD
AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG
AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
ZGlnaWNlcnQuY29tMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwTAYDVR0gBEUwQzA3Bglg
hkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29t
L0NQUzAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAK3dVOj5dlv4MzK2i233
lDYvyJ3slFY2X2HKTYGte8nbK6i5/fsDImMYihAkp6VaNY/en8WZ5qcrQPVLuJrJ
DSXT04NnMeZOQDUoj/NHAmdfCBB/h1bZ5OGK6Sf1h5Yx/5wR4f3TUoPgGlnU7EuP
ISLNdMRiDrXntcImDAiRvkh5GJuH4YCVE6XEntqaNIgGkRwxKSgnU3Id3iuFbW9F
UQ9Qqtb1GX91AJ7i4153TikGgYCdwYkBURD8gSVe8OAco6IfZOYt/TEwii1Ivi1C
qnuUlWpsF1LdQNIdfbW3TSe0BhQa7ifbVIfvPWHYOu3rkg1ZeMo6XRU9B4n5VyJY
RmE=
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAtIoralHk8ZKuamm21XAfu2vmAK3ygCQUb3I/k1VXUZLxI3Hx
i71yCVE73FKiZEUWBW4pnYweauwlgpXfG4M/1DqSBJm3ap/loM6zZ1lma0YjecpR
wLCGipYGCoeoHH7RHKOjoJKzPKMqtYDkUQSHEo/eVXKqSHgFDFcgl0dId3V5ywlX
u9Ho6/MNnMh60nkuvJl8Oy5QHXwtCQGUwf8McHIrUp5yFRG1esDMxgQ+Smv1qa5y
YCWuz62+Sh4mYeFBuBLNiaSBwCzGCyHikAlAiD9TZlvAJXvp4WPZA7SXkhCGPv3U
aA46T5JCGWpTKfuadMg0NSyDuKQ2cZ6CNrxSxQIDAQABAoIBAA5RZOcOLp9/+Agl
cSQVO9cD1B5arUA/XEWIZIVdP8sO4cPjXfosoJYflKVBAnL8TaZJmdBOU/071C+6
jhKjApVkvb7SqAqzOqVZrz2zh91bFyYqBvjGpyznf/wmzQzRe+kPC0OJTuCwugrh
+Xl5Z/LvaP0S1nFf31qesE1/ED609pvWtdUWj2oop9zORBzjaGcXeOKNqX8XnQCp
rJ487wcnuL8Nnuu+vuh8dC9o7zwjGrKwXhM3sijLp4q9VOF9fW2bYLyfLUR3FCFq
JPrQXe1mY28w+uIlnEdPkwgGIJbzur601BuzU9ZH5MSl7iYV319MPz+rtizFJxLm
OCg5hcMCgYEA7RrgQsrSj4fhi0VVkQ3fFeLJPQlvVcMK0OOYxP3cKQYSgXo79bQK
i9yRCRVS2UTGh8B3MGbPr+sRINokeYtjWxGDOwAlkye9kRiCKzzZOrlwDAY/SEDZ
HH5mVnv9UHtxCFSUub67IVWH7wCmoprKEK/loyvJFiE5jsxVmNZl3o8CgYEAwu1R
nBsETJLuN5/fB9rW3NvjaHGT1B6LQ1uIBmsU6V71gBasawX/X3ENOoX1dQa2japp
x58pjLJuP/t4WgZ41PBagij2G83VdFBdvrcVSk7B1yt8rudxmcvo1K6FR2FG7DMU
9kkb9JKFT3/FM7LT+z4xXxJ0AoNg6QD1IIT5Y2sCgYEAyQUvOxGQISY334bh+8AB
8iE7MidsoA5jfiRoIiOEY7eFOwbyDOcexeMzh7rvacs4cmGH655O2Lv34p1vrSiz
DMO1OfFu6esYegqIWbYWCgar61XkkxJ/v/ueMhae9nwhoclr6mq9Zo6IV+Z6YIPR
awJmM8fsjXmPvfSZYaHr7hsCgYAPzW99SU9q6cp4JfTNzTb4BreD4xlJ7AP8PPJl
Gs9CMBmU/cGSl5ThZufco7mHeDjaeUNEFKooptp7Q2a5Xab0FFwyCyIQlPpGCLHg
4TTPplzelb7w6wBxqG9CtrdFVySJx4ZehQTIKgy2qjQRgeDfkGYuP++5uG7l1NcK
gN066wKBgDg2F4yMx4SBWL3aBjIVd+Zilm+pkU1ZGC1XcuLnqSSMcoDy/JMszQC1
SEdDxgnCGlkWjtaxNPwfZTPU0SIoX5dcAx1EaHDJ+ibiIKGtM8forSCiP8fNz0oR
SYNZaYVuDJb1SFk9vKJMuIIFWS+AM6rlcOpNu6A0+1MbstM4BvwS
-----END RSA PRIVATE KEY-----

View File

@ -1,15 +0,0 @@
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
client_max_body_size 100m;
include /etc/nginx/sites-enabled/*;#主要是这个地方,把新建的两配置文件包含进来
server {
listen 80 default_server;
server_name _;
return 404;
}
}

View File

@ -1,46 +0,0 @@
## 将HTTP请求全部重定向至HTTPS
server {
listen 80;
server_name api.django-vue-admin.com;
charset utf-8;
access_log /var/log/nginx/api-80.access.log;
error_log /var/log/nginx/api-80.error.log;
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
server {
listen 443 ssl;
server_name api.django-vue-admin.com;
root /dvadmin-backend/;#项目路径
charset utf-8;
ssl_certificate /nginx/keys/api.django-vue-admin.com.crt;#.pem证书路径
ssl_certificate_key /nginx/keys/api.django-vue-admin.com.key;#.key证书路径
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header Host $proxy_host;
proxy_set_header X-DTS-SCHEMA api;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
location / {
root html/www;
include uwsgi_params;
uwsgi_pass dvadmin-django:8000;
uwsgi_param UWSGI_SCRIPT home.wsgi;
uwsgi_param UWSGI_CHDIR /dvadmin-backend/;#项目路径
}
# Django media
location /media {
alias /dvadmin-backend/media; # your Django project's media files - amend as required
}
# Django static
location /static {
alias /dvadmin-backend/static; # your Django project's static files - amend as required
}
access_log /var/log/nginx/api-443.access.log;
error_log /var/log/nginx/api-443.error.log;
}

View File

@ -1,46 +0,0 @@
## 将HTTP请求全部重定向至HTTPS
server {
listen 80;
server_name daily.api.django-vue-admin.com;
charset utf-8;
access_log /var/log/nginx/api-80.access.log;
error_log /var/log/nginx/api-80.error.log;
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
server {
listen 443 ssl;
server_name daily.api.django-vue-admin.com;
root /dvadmin-backend/;#项目路径
charset utf-8;
ssl_certificate /nginx/keys/daily.api.django-vue-admin.com.crt;#.pem证书路径
ssl_certificate_key /nginx/keys/daily.api.django-vue-admin.com.key;#.key证书路径
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header Host $proxy_host;
proxy_set_header X-DTS-SCHEMA daily.api;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
location / {
root html/www;
include uwsgi_params;
uwsgi_pass dvadmin-django:8000;
uwsgi_param UWSGI_SCRIPT home.wsgi;
uwsgi_param UWSGI_CHDIR /dvadmin-backend/;#项目路径
}
# Django media
location /media {
alias /dvadmin-backend/media; # your Django project's media files - amend as required
}
# Django static
location /static {
alias /dvadmin-backend/static; # your Django project's static files - amend as required
}
access_log /var/log/nginx/daily.api-443.access.log;
error_log /var/log/nginx/daily.api-443.error.log;
}

View File

@ -1,44 +0,0 @@
## 将HTTP请求全部重定向至HTTPS
server {
listen 80;
server_name daily.demo.django-vue-admin.com;
charset utf-8;
access_log /var/log/nginx/daily.demo-80.access.log;
error_log /var/log/nginx/daily.demo-80.error.log;
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
server {
listen 443 ssl;
server_name daily.demo.django-vue-admin.com;
root /vadmin-doc/;#项目路径
charset utf-8;
ssl_certificate /nginx/keys/daily.demo.django-vue-admin.com.crt;#.pem证书路径
ssl_certificate_key /nginx/keys/daily.demo.django-vue-admin.com.key;#.key证书路径
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header Host $proxy_host;
proxy_set_header X-DTS-SCHEMA daily.demo;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
location / {
try_files $uri $uri/ @router;#需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
root /dvadmin-ui/dist/;
add_header Cache-Control max-age=no-cache;
index index.html index.php index.htm;
}
location ~* \.(css|js|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff)$ {
access_log off;
add_header Cache-Control max-age=604800;
root /dvadmin-ui/dist/;
index index.html index.php index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
access_log /var/log/nginx/daily.demo-443.access.log;
error_log /var/log/nginx/daily.demo-443.error.log;
}

View File

@ -1,44 +0,0 @@
## 将HTTP请求全部重定向至HTTPS
server {
listen 80;
server_name demo.django-vue-admin.com;
charset utf-8;
access_log /var/log/nginx/demo-80.access.log;
error_log /var/log/nginx/demo-80.error.log;
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
server {
listen 443 ssl;
server_name demo.django-vue-admin.com;
root /vadmin-doc/;#项目路径
charset utf-8;
ssl_certificate /nginx/keys/demo.django-vue-admin.com.crt;#.pem证书路径
ssl_certificate_key /nginx/keys/demo.django-vue-admin.com.key;#.key证书路径
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header Host $proxy_host;
proxy_set_header X-DTS-SCHEMA demo;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
location / {
try_files $uri $uri/ @router;#需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
root /dvadmin-ui/dist/;
add_header Cache-Control max-age=no-cache;
index index.html index.php index.htm;
}
location ~* \.(css|js|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff)$ {
access_log off;
add_header Cache-Control max-age=604800;
root /dvadmin-ui/dist/;
index index.html index.php index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
access_log /var/log/nginx/demo-443.access.log;
error_log /var/log/nginx/demo-443.error.log;
}

View File

@ -1,37 +0,0 @@
## 将HTTP请求全部重定向至HTTPS
server {
listen 80;
server_name django-vue-admin.com;
charset utf-8;
access_log /var/log/nginx/www-80.access.log;
error_log /var/log/nginx/www-80.error.log;
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
server {
listen 443 ssl;
server_name django-vue-admin.com;
root /vadmin-doc/;#项目路径
charset utf-8;
ssl_certificate /nginx/keys/www.django-vue-admin.com.crt;#.pem证书路径
ssl_certificate_key /nginx/keys/www.django-vue-admin.com.key;#.key证书路径
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header Host $proxy_host;
proxy_set_header X-DTS-SCHEMA www;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
location / {
try_files $uri $uri/ @router;#需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
root /dvadmin-doc/docs/.vuepress/dist/;
index index.html index.php index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
access_log /var/log/nginx/www-443.access.log;
error_log /var/log/nginx/www-443.error.log;
}

View File

@ -1,37 +0,0 @@
## 将HTTP请求全部重定向至HTTPS
server {
listen 80;
server_name www.django-vue-admin.com;
charset utf-8;
access_log /var/log/nginx/www-80.access.log;
error_log /var/log/nginx/www-80.error.log;
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
server {
listen 443 ssl;
server_name www.django-vue-admin.com;
root /vadmin-doc/;#项目路径
charset utf-8;
ssl_certificate /nginx/keys/www.django-vue-admin.com.crt;#.pem证书路径
ssl_certificate_key /nginx/keys/www.django-vue-admin.com.key;#.key证书路径
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header Host $proxy_host;
proxy_set_header X-DTS-SCHEMA www;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
location / {
try_files $uri $uri/ @router;#需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
root /dvadmin-doc/docs/.vuepress/dist/;
index index.html index.php index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
access_log /var/log/nginx/www-443.access.log;
error_log /var/log/nginx/www-443.error.log;
}

View File

@ -1,5 +0,0 @@
FROM node:12
COPY ./dvadmin-ui/package.json /
RUN npm install --registry=https://registry.npm.taobao.org
#RUN npm run build:prod
#CMD ["npm","run","dev"]

View File

@ -1 +0,0 @@

View File

@ -21,7 +21,7 @@ from conf.env import *
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,os.path.join(BASE_DIR,'apps')) sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
@ -63,6 +63,7 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'vadmin.op_drf.middleware.ApiLoggingMiddleware', # 用于记录API访问日志 'vadmin.op_drf.middleware.ApiLoggingMiddleware', # 用于记录API访问日志
'vadmin.op_drf.middleware.PermissionModeMiddleware', # 权限中间件
] ]
# 允许跨域源 # 允许跨域源
CORS_ORIGIN_ALLOW_ALL = CORS_ORIGIN_ALLOW_ALL CORS_ORIGIN_ALLOW_ALL = CORS_ORIGIN_ALLOW_ALL
@ -202,7 +203,7 @@ LOGGING = {
'loggers': { 'loggers': {
# default日志 # default日志
'': { '': {
'handlers': ['console','error','file'], 'handlers': ['console', 'error', 'file'],
'level': 'INFO', 'level': 'INFO',
}, },
# 数据库相关日志 # 数据库相关日志
@ -239,7 +240,6 @@ else:
} }
} }
connect(MONGO_DATABASE_NAME, host=os.getenv('MONGO_HOST') or MONGO_HOST, port=MONGO_PORT, serverSelectionTimeoutMS=1000, connect=False)
# redis 缓存 # redis 缓存
REDIS_URL = f'redis://:{REDIS_PASSWORD if REDIS_PASSWORD else ""}@{os.getenv("REDIS_HOST") or REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}' REDIS_URL = f'redis://:{REDIS_PASSWORD if REDIS_PASSWORD else ""}@{os.getenv("REDIS_HOST") or REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}'
CACHES = { CACHES = {
@ -301,24 +301,30 @@ USERNAME_FIELD = 'username'
# ************** 登录验证码配置 ************** # # ************** 登录验证码配置 ************** #
# ================================================= # # ================================================= #
CAPTCHA_STATE = CAPTCHA_STATE CAPTCHA_STATE = CAPTCHA_STATE
#字母验证码 # 字母验证码
CAPTCHA_IMAGE_SIZE = (160, 60) # 设置 captcha 图片大小 CAPTCHA_IMAGE_SIZE = (160, 60) # 设置 captcha 图片大小
CAPTCHA_LENGTH = 4 # 字符个数 CAPTCHA_LENGTH = 4 # 字符个数
CAPTCHA_TIMEOUT = 1 # 超时(minutes) CAPTCHA_TIMEOUT = 1 # 超时(minutes)
#加减乘除验证码 # 加减乘除验证码
CAPTCHA_OUTPUT_FORMAT = '%(image)s %(text_field)s %(hidden_field)s ' CAPTCHA_OUTPUT_FORMAT = '%(image)s %(text_field)s %(hidden_field)s '
CAPTCHA_FONT_SIZE = 40 # 字体大小 CAPTCHA_FONT_SIZE = 40 # 字体大小
CAPTCHA_FOREGROUND_COLOR = '#0033FF' # 前景色 CAPTCHA_FOREGROUND_COLOR = '#0033FF' # 前景色
CAPTCHA_BACKGROUND_COLOR = '#F5F7F4' # 背景色 CAPTCHA_BACKGROUND_COLOR = '#F5F7F4' # 背景色
CAPTCHA_NOISE_FUNCTIONS = ( CAPTCHA_NOISE_FUNCTIONS = (
# 'captcha.helpers.noise_arcs', # 线 # 'captcha.helpers.noise_arcs', # 线
# 'captcha.helpers.noise_dots', # 点 # 'captcha.helpers.noise_dots', # 点
) )
# CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge' # CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge'
CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge' CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'
API_LOG_ENABLE = True API_LOG_ENABLE = True
# API_LOG_METHODS = 'ALL' # ['POST', 'DELETE'] # API_LOG_METHODS = 'ALL' # ['POST', 'DELETE']
# API_LOG_METHODS = ['POST', 'DELETE'] # ['POST', 'DELETE'] # API_LOG_METHODS = ['POST', 'DELETE'] # ['POST', 'DELETE']
BROKER_URL = f'redis://:{REDIS_PASSWORD if REDIS_PASSWORD else ""}@{os.getenv("REDIS_HOST") or REDIS_HOST}:{REDIS_PORT}/2' #Broker使用Redis, 使用0数据库(暂时不是很清楚原理) BROKER_URL = f'redis://:{REDIS_PASSWORD if REDIS_PASSWORD else ""}@{os.getenv("REDIS_HOST") or REDIS_HOST}:' \
CELERYBEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler' #Backend数据库 f'{REDIS_PORT}/{locals().get("CELERY_DB", 2)}' # Broker使用Redis
CELERYBEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler' # Backend数据库
# ================================================= #
# ************** 其他配置 ************** #
# ================================================= #
# 接口权限
INTERFACE_PERMISSION = {locals().get("INTERFACE_PERMISSION", False)}

View File

@ -22,7 +22,7 @@ from django.urls import re_path, include
from django.views.static import serve from django.views.static import serve
from rest_framework.views import APIView from rest_framework.views import APIView
from apps.vadmin.op_drf.response import SuccessResponse from vadmin.utils.response import SuccessResponse
class CaptchaRefresh(APIView): class CaptchaRefresh(APIView):

View File

@ -10,7 +10,6 @@ from django.utils import six
from mongoengine.queryset import visitor from mongoengine.queryset import visitor
from rest_framework.filters import BaseFilterBackend, SearchFilter, OrderingFilter from rest_framework.filters import BaseFilterBackend, SearchFilter, OrderingFilter
from ..permission.models import Dept
from ..utils.model_util import get_dept from ..utils.model_util import get_dept
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -119,7 +118,7 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
return queryset.none() return queryset.none()
# 1. 判断过滤的数据是否有创建人所在部门 "dept_belong_id" 字段 # 1. 判断过滤的数据是否有创建人所在部门 "dept_belong_id" 字段
if not hasattr(queryset.model, 'dept_belong_id'): if not getattr(queryset.model, 'dept_belong_id', None):
return queryset return queryset
# 2. 如果用户没有关联角色则返回本部门数据 # 2. 如果用户没有关联角色则返回本部门数据
@ -127,7 +126,7 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
return queryset.filter(dept_belong_id=user_dept_id) return queryset.filter(dept_belong_id=user_dept_id)
# 3. 根据所有角色 获取所有权限范围 # 3. 根据所有角色 获取所有权限范围
role_list = request.user.role.all().values('admin', 'dataScope') role_list = request.user.role.filter(status='1').values('admin', 'dataScope')
dataScope_list = [] dataScope_list = []
for ele in role_list: for ele in role_list:
# 3.1 判断用户是否为超级管理员角色/如果有1(所有数据) 则返回所有数据 # 3.1 判断用户是否为超级管理员角色/如果有1(所有数据) 则返回所有数据
@ -138,16 +137,15 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
# 4. 只为仅本人数据权限时只返回过滤本人数据,并且部门为自己本部门(考虑到用户会变部门,只能看当前用户所在的部门数据) # 4. 只为仅本人数据权限时只返回过滤本人数据,并且部门为自己本部门(考虑到用户会变部门,只能看当前用户所在的部门数据)
if dataScope_list == ['5']: if dataScope_list == ['5']:
return queryset.filter(creator=request.user, dept_belong_id=request.user.dept_id) return queryset.filter(creator=request.user, dept_belong_id=user_dept_id)
# 5. 自定数据权限 获取部门,根据部门过滤 # 5. 自定数据权限 获取部门,根据部门过滤
dept_list = [] dept_list = []
for ele in dataScope_list: for ele in dataScope_list:
if ele == '2': if ele == '2':
dept_list.extend(request.user.role.all().values_list('dept__id', flat=True)) dept_list.extend(request.user.role.filter(status='1').values_list('dept__id', flat=True))
elif ele == '3': elif ele == '3':
dept_list.append(user_dept_id) dept_list.append(user_dept_id)
elif ele == '4': elif ele == '4':
dept_list.extend(get_dept(user_dept_id,)) dept_list.extend(get_dept(user_dept_id, ))
return queryset.filter(dept_belong_id__in=list(set(dept_list))) return queryset.filter(dept_belong_id__in=list(set(dept_list)))

View File

@ -1,14 +1,21 @@
""" """
django中间件 django中间件
""" """
import json
import logging
import os
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.utils.deprecation import MiddlewareMixin from django.utils.deprecation import MiddlewareMixin
from apps.vadmin.permission.models import Menu
from apps.vadmin.system.models import OperationLog from apps.vadmin.system.models import OperationLog
from ..utils.request_util import get_request_ip, get_request_data, get_request_path, get_browser, get_os, \ from ..utils.request_util import get_request_ip, get_request_data, get_request_path, get_browser, get_os, \
get_login_location get_login_location, get_request_canonical_path, get_request_user, get_verbose_name
from ..utils.response import ErrorJsonResponse
logger = logging.getLogger(__name__)
class ApiLoggingMiddleware(MiddlewareMixin): class ApiLoggingMiddleware(MiddlewareMixin):
@ -36,9 +43,16 @@ class ApiLoggingMiddleware(MiddlewareMixin):
body['password'] = '*' * len(body['password']) body['password'] = '*' * len(body['password'])
if not hasattr(response, 'data') or not isinstance(response.data, dict): if not hasattr(response, 'data') or not isinstance(response.data, dict):
response.data = {} response.data = {}
if not response.data and response.content:
try:
content = json.loads(response.content.decode())
response.data = content if isinstance(content, dict) else {}
except:
pass
user = get_request_user(request)
info = { info = {
'request_ip': getattr(request, 'request_ip', 'unknown'), 'request_ip': getattr(request, 'request_ip', 'unknown'),
'creator': request.user, 'creator': user if not isinstance(user, AnonymousUser) else None,
'dept_belong_id': getattr(request.user, 'dept_id', None), 'dept_belong_id': getattr(request.user, 'dept_id', None),
'request_method': request.method, 'request_method': request.method,
'request_path': request.request_path, 'request_path': request.request_path,
@ -52,11 +66,14 @@ class ApiLoggingMiddleware(MiddlewareMixin):
'json_result': {"code": response.data.get('code'), "msg": response.data.get('msg')}, 'json_result': {"code": response.data.get('code'), "msg": response.data.get('msg')},
'request_modular': request.session.get('model_name'), 'request_modular': request.session.get('model_name'),
} }
if isinstance(request.user, AnonymousUser):
info['creator'] = None
log = OperationLog(**info) log = OperationLog(**info)
log.save() log.save()
def process_view(self, request, view_func, view_args, view_kwargs):
if hasattr(view_func, 'cls') and hasattr(view_func.cls, 'queryset'):
request.session['model_name'] = get_verbose_name(view_func.cls.queryset)
return
def process_request(self, request): def process_request(self, request):
self.__handle_request(request) self.__handle_request(request)
@ -77,3 +94,76 @@ class PermissionModeMiddleware(MiddlewareMixin):
""" """
权限模式拦截判断 权限模式拦截判断
""" """
def process_request(self, request):
return
def has_interface_permission(self, request, method, view_path, user=None):
"""
接口权限验证,优先级:
(1)接口是否接入权限管理, :继续; :通过
(2)认证的user是否superuser, :通过; :继续
(3)user的角色有该接口权限, :通过, :不通过
auth_code含义: auth_code >=0, 表示接口认证通过; auth_code < 0, 表示无接口访问权限, 具体含义如下
-1:
-10: 该请求已认证的用户没有这个接口的访问权限
0:
1: 白名单
10: 该接口没有录入权限系统, 放行 请求中认证的用户为超级管理员, 直接放行
20: 请求中认证的用户是superuser放行
30: 请求中认证的用户对应的角色中,某个角色包含了该接口的访问权限, 放行
1. 先获取所有录入系统的接口
2 判断此用户是否为 superuser
3. 获取此用户所请求的接口
4. 获取此用户关联角色所有有权限的接口
:param interface: 接口模型
:param path: 接口路径
:param method: 请求方法
:param project: 接口所属项目
:param args:
:param kwargs:
:return:
"""
interface_dict = Menu.get_interface_dict()
# (1) 接口是否接入权限管理, 是:继续; 否:通过
if not view_path in interface_dict.get(method, []):
return 10
# (2)认证的user是否superuser, 是:通过; 否:继续
if user.is_superuser or (hasattr(user, 'role') and user.role.filter(status='1', admin=True).count()):
return 20
# (3)user的角色有该接口权限, 是:通过, 否:不通过
if view_path in user.get_user_interface_dict.get(method, []):
return 30
return -10
def process_view(self, request, view_func, view_args, view_kwargs):
# 判断环境变量中,是否为演示模式(正常可忽略此判断)
white_list = ['/admin/logout/', '/admin/login/']
if os.getenv('DEMO_ENV') and not request.method in ['GET', 'OPTIONS'] and request.path not in white_list:
return ErrorJsonResponse(data={}, msg=f'演示模式,不允许操作!')
if not settings.INTERFACE_PERMISSION:
return
user = get_request_user(request)
if user and not isinstance(user, AnonymousUser):
method = request.method.upper()
if method == 'GET': # GET 不设置接口权限
return
view_path = get_request_canonical_path(request, *view_args, **view_kwargs)
auth_code = self.has_interface_permission(request, method, view_path, user)
logger.info(f"[{user.username}] {method}:{view_path}, 权限认证:{auth_code}")
if auth_code >= 0:
return
return ErrorJsonResponse(data={}, msg=f'无接口访问权限!')
def process_response(self, request, response):
"""
主要请求处理完之后记录
:param request:
:param response:
:return:
"""
return response

View File

@ -320,12 +320,13 @@ class ImportSerializerMixin:
updateSupport = request.data.get('updateSupport') updateSupport = request.data.get('updateSupport')
# 从excel中组织对应的数据结构然后使用序列化器保存 # 从excel中组织对应的数据结构然后使用序列化器保存
data = excel_to_data(request.data.get('file_url'), self.import_field_data) data = excel_to_data(request.data.get('file_url'), self.import_field_data)
unique_list = [ele.attname for ele in self.get_queryset().model._meta.get_fields() if queryset = self.filter_queryset(self.get_queryset())
unique_list = [ele.attname for ele in queryset.model._meta.get_fields() if
hasattr(ele, 'unique') and ele.unique == True] hasattr(ele, 'unique') and ele.unique == True]
for ele in data: for ele in data:
# 获取 unique 字段 # 获取 unique 字段
filter_dic = {i: ele.get(i) for i in list(set(self.import_field_data.keys()) & set(unique_list))} filter_dic = {i: ele.get(i) for i in list(set(self.import_field_data.keys()) & set(unique_list))}
instance = self.get_queryset().filter(**filter_dic).first() instance = queryset.filter(**filter_dic).first()
if instance and not updateSupport: if instance and not updateSupport:
continue continue
if not filter_dic: if not filter_dic:
@ -357,6 +358,7 @@ class ExportSerializerMixin:
"'%s' 请配置对应的导出模板字段。" "'%s' 请配置对应的导出模板字段。"
% self.__class__.__name__ % self.__class__.__name__
) )
data = self.export_serializer_class(self.get_queryset(), many=True).data queryset = self.filter_queryset(self.get_queryset())
data = self.export_serializer_class(queryset, many=True).data
return SuccessResponse(export_excel_save_model(request, self.export_field_data, data, return SuccessResponse(export_excel_save_model(request, self.export_field_data, data,
f'导出{get_verbose_name(self.get_queryset())}.xls')) f'导出{get_verbose_name(queryset)}.xls'))

View File

@ -20,6 +20,7 @@ class CustomModelSerializer(ModelSerializer):
# 添加默认时间返回格式 # 添加默认时间返回格式
create_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True) create_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
update_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True) update_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
creator_name = serializers.SlugRelatedField(slug_field="username", source="creator", read_only=True)
def __init__(self, instance=None, data=empty, request=None, **kwargs): def __init__(self, instance=None, data=empty, request=None, **kwargs):
super().__init__(instance, data, **kwargs) super().__init__(instance, data, **kwargs)

View File

@ -1,4 +1,5 @@
from django.db.models import IntegerField, ForeignKey, CharField, CASCADE from django.core.cache import cache
from django.db.models import IntegerField, ForeignKey, CharField, CASCADE, Q
from ...op_drf.models import CoreModel from ...op_drf.models import CoreModel
@ -34,6 +35,31 @@ class Menu(CoreModel):
visible = CharField(max_length=8, verbose_name="显示状态") visible = CharField(max_length=8, verbose_name="显示状态")
isCache = CharField(max_length=8, verbose_name="是否缓存") isCache = CharField(max_length=8, verbose_name="是否缓存")
@classmethod
def get_interface_dict(cls):
"""
获取所有接口列表
:return:
"""
interface_dict = cache.get('permission_interface_dict', {})
if not interface_dict:
for ele in Menu.objects.filter(~Q(interface_path=''), ~Q(interface_path=None), status='1', ).values(
'interface_path', 'interface_method'):
if ele.get('interface_method') in interface_dict:
interface_dict[ele.get('interface_method', '')].append(ele.get('interface_path'))
else:
interface_dict[ele.get('interface_method', '')] = [ele.get('interface_path')]
cache.set('permission_interface_dict', interface_dict, 84600)
return interface_dict
@classmethod
def delete_cache(cls):
"""
清空缓存中的接口列表
:return:
"""
cache.delete('permission_interface_dict')
class Meta: class Meta:
verbose_name = '菜单管理' verbose_name = '菜单管理'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name

View File

@ -1,12 +1,13 @@
from uuid import uuid4 from uuid import uuid4
from django.contrib.auth.models import UserManager, AbstractUser from django.contrib.auth.models import UserManager, AbstractUser
from django.core.cache import cache
from django.db.models import IntegerField, ForeignKey, CharField, TextField, ManyToManyField, CASCADE from django.db.models import IntegerField, ForeignKey, CharField, TextField, ManyToManyField, CASCADE
from ...op_drf.fields import CreateDateTimeField, UpdateDateTimeField from ...op_drf.models import CoreModel
class UserProfile(AbstractUser): class UserProfile(AbstractUser, CoreModel):
USER_TYPE_CHOICES = ( USER_TYPE_CHOICES = (
(0, "后台用户"), (0, "后台用户"),
(1, "前台用户"), (1, "前台用户"),
@ -24,9 +25,30 @@ class UserProfile(AbstractUser):
post = ManyToManyField(to='Post', verbose_name='关联岗位', db_constraint=False) post = ManyToManyField(to='Post', verbose_name='关联岗位', db_constraint=False)
role = ManyToManyField(to='Role', verbose_name='关联角色', db_constraint=False) role = ManyToManyField(to='Role', verbose_name='关联角色', db_constraint=False)
dept = ForeignKey(to='Dept', verbose_name='归属部门', on_delete=CASCADE, db_constraint=False, null=True, blank=True) dept = ForeignKey(to='Dept', verbose_name='归属部门', on_delete=CASCADE, db_constraint=False, null=True, blank=True)
dept_belong_id = CharField(max_length=64, verbose_name="数据归属部门", null=True, blank=True)
create_datetime = CreateDateTimeField() @property
update_datetime = UpdateDateTimeField() def get_user_interface_dict(self):
interface_dict = cache.get(f'permission_interface_dict{self.username}', {})
if not interface_dict:
for ele in self.role.filter(status='1', menu__status='1').values('menu__interface_path',
'menu__interface_method').distinct():
interface_path = ele.get('menu__interface_path')
if interface_path is None or interface_path == '':
continue
if ele.get('menu__interface_method') in interface_dict:
interface_dict[ele.get('menu__interface_method', '')].append(interface_path)
else:
interface_dict[ele.get('menu__interface_method', '')] = [interface_path]
cache.set(f'permission_interface_dict_{self.username}', interface_dict, 84600)
return interface_dict
@property
def delete_cache(self):
"""
清空缓存中的接口列表
:return:
"""
return cache.delete(f'permission_interface_dict_{self.username}')
class Meta: class Meta:
verbose_name = '用户管理' verbose_name = '用户管理'

View File

@ -0,0 +1,115 @@
"""
常用的Permission以及DRF的Permission
@author: ruoxing
"""
import logging
from django.contrib.auth import get_user_model
from rest_framework.permissions import (BasePermission,
)
from rest_framework.request import Request
from rest_framework.views import APIView
from ..utils.model_util import get_dept
logger = logging.getLogger(__name__)
User = get_user_model()
class CustomPermission(BasePermission):
def __init__(self, message=None) -> None:
super().__init__()
self.message = getattr(self.__class__, 'message', '无权限')
self.user: User = None
def init_permission(self, request: Request, view: APIView):
self.user = request.user
def has_permission(self, request: Request, view: APIView):
return True
def has_object_permission(self, request: Request, view: APIView, obj):
return True
class CommonPermission(CustomPermission):
"""
通用权限类判断用户是否有这条数据的查询权限如没有则直接没有操作权限
0. 获取用户的部门id没有部门则返回 False
1. 判断过滤的数据是否有创建人所在部门 "dept_belong_id" 字段没有则返回 True
2. 如果用户没有关联角色则校验该数据是否属于本部门
3. 根据所有角色 获取所有权限范围
3.1 判断用户是否为超级管理员角色/如果有1(所有数据) 则有权限操作
4. 只为仅本人数据权限时只有操作本人数据权限并且部门为自己本部门(考虑到用户会变部门只能看当前用户所在的部门数据)
5. 自定数据权限 获取部门根据部门判断是否有权限操作
"""
message = '没有有操作权限'
def check_queryset(self, request, instance):
# 0. 获取用户的部门id没有部门则返回 False
user_dept_id = getattr(request.user, 'dept_id')
if not user_dept_id:
self.message = "该用户无部门,无权限操作!"
return False
# 1. 判断过滤的数据是否有创建人所在部门 "dept_belong_id" 字段,没有则返回 True
if not getattr(instance, 'dept_belong_id', None):
return True
# 2. 如果用户没有关联角色则校验该数据是否属于本部门
if not hasattr(request.user, 'role'):
self.message = "该用户无角色,无权限操作!"
return False
# 3. 根据所有角色 获取所有权限范围
role_list = request.user.role.filter(status='1').values('admin', 'dataScope')
dataScope_list = []
for ele in role_list:
# 3.1 判断用户是否为超级管理员角色/如果有1(所有数据) 则有权限操作
if '1' == ele.get('dataScope') or ele.get('admin') == True:
return True
dataScope_list.append(ele.get('dataScope'))
dataScope_list = list(set(dataScope_list))
# 4. 只为仅本人数据权限时只有操作本人数据权限,并且部门为自己本部门(考虑到用户会变部门,只能看当前用户所在的部门数据)
if dataScope_list == ['5']:
return int(instance.dept_belong_id) == user_dept_id and request.user == instance.creator
# 5. 自定数据权限 获取部门,根据部门判断,是否有权限操作
dept_list = []
for ele in dataScope_list:
if ele == '2':
dept_list.extend(request.user.role.filter(status='1').values_list('dept__id', flat=True))
elif ele == '3':
dept_list.append(user_dept_id)
elif ele == '4':
dept_list.extend(get_dept(user_dept_id, ))
return int(instance.dept_belong_id) in list(set(dept_list))
def has_permission(self, request: Request, view: APIView):
return True
def has_object_permission(self, request: Request, view: APIView, instance):
self.message = f"没有此数据操作权限!"
res = self.check_queryset(request, instance)
return res
class DeptDestroyPermission(CustomPermission):
"""
部门删除权限校验判断部门下是否有用户存在存在不可删除
"""
message = '没有有操作权限'
def has_permission(self, request: Request, view: APIView):
return True
def check_queryset(self, request, instance):
if instance.values_list('userprofile', flat=True):
self.message = "该部门下有关联用户,无法删除!"
return False
return True
def has_object_permission(self, request: Request, view: APIView, instance):
res = self.check_queryset(request, instance)
return res

View File

@ -37,6 +37,10 @@ class MenuCreateUpdateSerializer(CustomModelSerializer):
# raise APIException(message=f'仅Manger能创建/更新角色为公共角色') # raise APIException(message=f'仅Manger能创建/更新角色为公共角色')
return super().validate(attrs) return super().validate(attrs)
def save(self, **kwargs):
Menu.delete_cache()
return super().save(**kwargs)
class Meta: class Meta:
model = Menu model = Menu
fields = '__all__' fields = '__all__'
@ -91,7 +95,7 @@ class DeptTreeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Dept model = Dept
fields = ('id', 'label', 'parentId') fields = ('id', 'label', 'parentId', 'status')
# ================================================= # # ================================================= #
@ -216,14 +220,14 @@ class UserProfileSerializer(CustomModelSerializer):
unread_msg_count = serializers.SerializerMethodField(read_only=True) unread_msg_count = serializers.SerializerMethodField(read_only=True)
def get_admin(self, obj: UserProfile): def get_admin(self, obj: UserProfile):
role_list = obj.role.all().values_list('admin', flat=True) role_list = obj.role.filter(status='1').values_list('admin', flat=True)
if True in list(set(role_list)): if True in list(set(role_list)):
return True return True
return False return False
def get_unread_msg_count(self, obj: UserProfile): def get_unread_msg_count(self, obj: UserProfile):
return MessagePush.objects.filter(status='2').exclude(user=obj, return MessagePush.objects.filter(status='2').exclude(messagepushuser_message_push__is_read=True,
messagepushuser_message_push__is_read=True).count() messagepushuser_message_push__user=obj).count()
class Meta: class Meta:
model = UserProfile model = UserProfile
@ -261,7 +265,7 @@ class UserProfileCreateUpdateSerializer(CustomModelSerializer):
}) })
def get_admin(self, obj: UserProfile): def get_admin(self, obj: UserProfile):
role_list = obj.role.all().values_list('admin', flat=True) role_list = obj.role.filter(status='1').values_list('admin', flat=True)
if True in list(set(role_list)): if True in list(set(role_list)):
return True return True
return False return False

View File

@ -2,6 +2,7 @@ from django.contrib.auth import authenticate
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.views import APIView from rest_framework.views import APIView
from .permissions import CommonPermission, DeptDestroyPermission
from ..op_drf.filters import DataLevelPermissionsFilter from ..op_drf.filters import DataLevelPermissionsFilter
from ..op_drf.viewsets import CustomModelViewSet from ..op_drf.viewsets import CustomModelViewSet
from ..permission.filters import MenuFilter, DeptFilter, PostFilter, RoleFilter, UserProfileFilter from ..permission.filters import MenuFilter, DeptFilter, PostFilter, RoleFilter, UserProfileFilter
@ -12,7 +13,6 @@ from ..permission.serializers import UserProfileSerializer, MenuSerializer, Role
PostSimpleSerializer, RoleSimpleSerializer, ExportUserProfileSerializer, ExportRoleSerializer, ExportPostSerializer, \ PostSimpleSerializer, RoleSimpleSerializer, ExportUserProfileSerializer, ExportRoleSerializer, ExportPostSerializer, \
UserProfileImportSerializer UserProfileImportSerializer
from ..system.models import DictDetails from ..system.models import DictDetails
from ..utils.export_excel import export_excel_save_model
from ..utils.response import SuccessResponse, ErrorResponse from ..utils.response import SuccessResponse, ErrorResponse
@ -25,6 +25,7 @@ class GetUserProfileView(APIView):
user_dict = UserProfileSerializer(request.user).data user_dict = UserProfileSerializer(request.user).data
permissions_list = ['*:*:*'] if user_dict.get('admin') else Menu.objects.filter( permissions_list = ['*:*:*'] if user_dict.get('admin') else Menu.objects.filter(
role__userprofile=request.user).values_list('perms', flat=True) role__userprofile=request.user).values_list('perms', flat=True)
delete_cache = request.user.delete_cache
return SuccessResponse({ return SuccessResponse({
'permissions': [ele for ele in permissions_list if ele], 'permissions': [ele for ele in permissions_list if ele],
'roles': Role.objects.filter(userprofile=request.user).values_list('roleKey', flat=True), 'roles': Role.objects.filter(userprofile=request.user).values_list('roleKey', flat=True),
@ -47,7 +48,10 @@ class GetRouters(APIView):
return dict return dict
def get(self, request, format=None): def get(self, request, format=None):
menus = Menu.objects.filter(role__userprofile=request.user) \ kwargs = {}
if not request.user.is_superuser:
kwargs['role__userprofile'] = request.user
menus = Menu.objects.filter(**kwargs) \
.exclude(menuType='2').values('id', 'name', 'web_path', 'visible', 'status', 'isFrame', 'component_path', .exclude(menuType='2').values('id', 'name', 'web_path', 'visible', 'status', 'isFrame', 'component_path',
'icon', 'parentId', 'orderNum', 'isCache').distinct() 'icon', 'parentId', 'orderNum', 'isCache').distinct()
data = [] data = []
@ -78,9 +82,9 @@ class MenuModelViewSet(CustomModelViewSet):
update_serializer_class = MenuCreateUpdateSerializer update_serializer_class = MenuCreateUpdateSerializer
filter_class = MenuFilter filter_class = MenuFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
# update_extra_permission_classes = (IsManagerPermission,) update_extra_permission_classes = (CommonPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,) destroy_extra_permission_classes = (CommonPermission,)
# create_extra_permission_classes = (IsManagerPermission,) create_extra_permission_classes = (CommonPermission,)
search_fields = ('name',) search_fields = ('name',)
ordering = 'create_datetime' # 默认排序 ordering = 'create_datetime' # 默认排序
@ -127,9 +131,9 @@ class DeptModelViewSet(CustomModelViewSet):
update_serializer_class = DeptCreateUpdateSerializer update_serializer_class = DeptCreateUpdateSerializer
filter_class = DeptFilter filter_class = DeptFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
# update_extra_permission_classes = (IsManagerPermission,) update_extra_permission_classes = (CommonPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,) destroy_extra_permission_classes = (CommonPermission, DeptDestroyPermission)
# create_extra_permission_classes = (IsManagerPermission,) create_extra_permission_classes = (CommonPermission,)
search_fields = ('deptName',) search_fields = ('deptName',)
ordering = 'create_datetime' # 默认排序 ordering = 'create_datetime' # 默认排序
@ -192,23 +196,13 @@ class PostModelViewSet(CustomModelViewSet):
update_serializer_class = PostCreateUpdateSerializer update_serializer_class = PostCreateUpdateSerializer
filter_class = PostFilter filter_class = PostFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
# update_extra_permission_classes = (IsManagerPermission,) update_extra_permission_classes = (CommonPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,) destroy_extra_permission_classes = (CommonPermission,)
# create_extra_permission_classes = (IsManagerPermission,) create_extra_permission_classes = (CommonPermission,)
search_fields = ('postName',) search_fields = ('postName',)
ordering = ['postSort', 'create_datetime'] # 默认排序 ordering = ['postSort', 'create_datetime'] # 默认排序
export_field_data = ['岗位序号', '岗位编码', '岗位名称', '岗位排序', '状态', '创建者', '修改者', '备注']
def export(self, request: Request, *args, **kwargs): export_serializer_class = ExportPostSerializer
"""
导出岗位
:param request:
:param args:
:param kwargs:
:return:
"""
field_data = ['岗位序号', '岗位编码', '岗位名称', '岗位排序', '状态', '创建者', '修改者', '备注']
data = ExportPostSerializer(Post.objects.all(), many=True).data
return SuccessResponse(export_excel_save_model(request, field_data, data, '导出岗位数据.xls'))
class RoleModelViewSet(CustomModelViewSet): class RoleModelViewSet(CustomModelViewSet):
@ -221,23 +215,13 @@ class RoleModelViewSet(CustomModelViewSet):
update_serializer_class = RoleCreateUpdateSerializer update_serializer_class = RoleCreateUpdateSerializer
filter_class = RoleFilter filter_class = RoleFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
# update_extra_permission_classes = (IsManagerPermission,) update_extra_permission_classes = (CommonPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,) destroy_extra_permission_classes = (CommonPermission,)
# create_extra_permission_classes = (IsManagerPermission,) create_extra_permission_classes = (CommonPermission,)
search_fields = ('roleName',) search_fields = ('roleName',)
ordering = 'create_datetime' # 默认排序 ordering = 'create_datetime' # 默认排序
export_field_data = ['角色序号', '角色名称', '角色权限', '角色排序', '数据范围', '角色状态', '创建者', '修改者', '备注']
def export(self, request: Request, *args, **kwargs): export_serializer_class = ExportRoleSerializer
"""
导出角色
:param request:
:param args:
:param kwargs:
:return:
"""
field_data = ['角色序号', '角色名称', '角色权限', '角色排序', '数据范围', '角色状态', '创建者', '修改者', '备注']
data = ExportRoleSerializer(Role.objects.all(), many=True).data
return SuccessResponse(export_excel_save_model(request, field_data, data, '导出角色数据.xls'))
class UserProfileModelViewSet(CustomModelViewSet): class UserProfileModelViewSet(CustomModelViewSet):
@ -259,9 +243,9 @@ class UserProfileModelViewSet(CustomModelViewSet):
'gender': '用户性别(男/女/未知)', 'gender': '用户性别(男/女/未知)',
'is_active': '帐号状态(启用/禁用)', 'password': '登录密码', 'dept': '部门ID', 'role': '角色ID', 'is_active': '帐号状态(启用/禁用)', 'password': '登录密码', 'dept': '部门ID', 'role': '角色ID',
'post': '岗位ID'} 'post': '岗位ID'}
# update_extra_permission_classes = (IsManagerPermission,) update_extra_permission_classes = (CommonPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,) destroy_extra_permission_classes = (CommonPermission,)
# create_extra_permission_classes = (IsManagerPermission,) create_extra_permission_classes = (CommonPermission,)
search_fields = ('username',) search_fields = ('username',)
ordering = 'create_datetime' # 默认排序 ordering = 'create_datetime' # 默认排序
@ -291,8 +275,8 @@ class UserProfileModelViewSet(CustomModelViewSet):
""" """
userId = request.query_params.get('userId') userId = request.query_params.get('userId')
data = { data = {
'posts': PostSimpleSerializer(Post.objects.all().order_by('postSort'), many=True).data, 'posts': PostSimpleSerializer(Post.objects.filter(status='1').order_by('postSort'), many=True).data,
'roles': RoleSimpleSerializer(Role.objects.all().order_by('roleSort'), many=True).data 'roles': RoleSimpleSerializer(Role.objects.filter(status='1').order_by('roleSort'), many=True).data
} }
if userId: if userId:
instance = self.queryset.get(id=userId) instance = self.queryset.get(id=userId)
@ -378,7 +362,7 @@ class UserProfileModelViewSet(CustomModelViewSet):
:return: :return:
""" """
instance = self.queryset.get(id=request.user.id) instance = self.queryset.get(id=request.user.id)
instance.mobile = request.data.get('newPassword', None) instance.password = request.data.get('newPassword', None)
if not authenticate(username=request.user.username, password=request.data.get('oldPassword', None)): if not authenticate(username=request.user.username, password=request.data.get('oldPassword', None)):
return ErrorResponse(msg='旧密码不正确!') return ErrorResponse(msg='旧密码不正确!')
instance.set_password(request.data.get('newPassword')) instance.set_password(request.data.get('newPassword'))

View File

@ -33,8 +33,8 @@
-- ---------------------------- -- ----------------------------
-- Records of permission_userprofile -- Records of permission_userprofile
-- ---------------------------- -- ----------------------------
INSERT INTO `permission_userprofile` (id, password, last_login, is_superuser, first_name, last_name, is_staff, is_active, date_joined, username, secret, email, mobile, avatar, name, gender, remark, user_type, create_datetime, update_datetime, dept_id, dept_belong_id) VALUES (1, 'pbkdf2_sha256$150000$OjTMSXJgkzrE$jEQCjWbIbXwpN4k2z0o8Yvou1UQGuoJALyL/kGDZFd4=', '2021-02-27 06:20:28.214775', 1, '', '', 1, 1, '2021-02-27 06:20:09.188689', 'admin', '3704adf3-380f-4c27-a8da-60420e8cb4ab', 'admin@qq.com', NULL, NULL, '管理员', '2', '1', 2, '2021-02-27 06:20:09.263192', '2021-02-27 09:14:30.009998', 8, 1); INSERT INTO `permission_userprofile` (id, password, last_login, is_superuser, first_name, last_name, is_staff, is_active, date_joined, username, secret, email, mobile, avatar, name, gender, remark, user_type, create_datetime, update_datetime, dept_id, dept_belong_id, creator_id) VALUES (1, 'pbkdf2_sha256$150000$OjTMSXJgkzrE$jEQCjWbIbXwpN4k2z0o8Yvou1UQGuoJALyL/kGDZFd4=', '2021-02-27 06:20:28.214775', 1, '', '', 1, 1, '2021-02-27 06:20:09.188689', 'admin', '3704adf3-380f-4c27-a8da-60420e8cb4ab', 'admin@qq.com', NULL, NULL, '管理员', '2', '1', 2, '2021-02-27 06:20:09.263192', '2021-02-27 09:14:30.009998', 1, 1, 1);
INSERT INTO `permission_userprofile` (id, password, last_login, is_superuser, first_name, last_name, is_staff, is_active, date_joined, username, secret, email, mobile, avatar, name, gender, remark, user_type, create_datetime, update_datetime, dept_id, dept_belong_id) VALUES (2, 'pbkdf2_sha256$150000$5Z9LSi7LpNms$xVguE/dOEpI4D95LjSaKm0xzG7vNSopUolANr8f/6/E=', NULL, 0, '', '', 0, 1, '2021-03-03 15:38:27.009893', 'dvadmin', 'b4c5d79a-f01c-4244-92f8-b5288eca1d50', NULL, NULL, NULL, '普通用户', '2', NULL, 0, '2021-03-03 15:38:27.010771', '2021-03-03 15:38:27.086069', 8, 1); INSERT INTO `permission_userprofile` (id, password, last_login, is_superuser, first_name, last_name, is_staff, is_active, date_joined, username, secret, email, mobile, avatar, name, gender, remark, user_type, create_datetime, update_datetime, dept_id, dept_belong_id, creator_id) VALUES (2, 'pbkdf2_sha256$150000$5Z9LSi7LpNms$xVguE/dOEpI4D95LjSaKm0xzG7vNSopUolANr8f/6/E=', NULL, 0, '', '', 0, 1, '2021-03-03 15:38:27.009893', 'dvadmin', 'b4c5d79a-f01c-4244-92f8-b5288eca1d50', NULL, NULL, NULL, '普通用户', '2', NULL, 0, '2021-03-03 15:38:27.010771', '2021-03-03 15:38:27.086069', 1, 1, 1);
-- ---------------------------- -- ----------------------------
-- Table structure for permission_userprofile_post -- Table structure for permission_userprofile_post
-- ---------------------------- -- ----------------------------

View File

@ -43,6 +43,7 @@ class SaveFileFilter(django_filters.rest_framework.FilterSet):
文件管理 简单过滤器 文件管理 简单过滤器
""" """
name = django_filters.CharFilter(lookup_expr='icontains') name = django_filters.CharFilter(lookup_expr='icontains')
type = django_filters.CharFilter(lookup_expr='icontains')
class Meta: class Meta:
model = SaveFile model = SaveFile

View File

@ -1,7 +1,6 @@
from ..models.config_settings import ConfigSettings from ..models.config_settings import ConfigSettings
from ..models.dict_data import DictData from ..models.dict_data import DictData
from ..models.dict_details import DictDetails from ..models.dict_details import DictDetails
from ..models.web_set import WebSet
from ..models.save_file import SaveFile from ..models.save_file import SaveFile
from ..models.message_push import MessagePush from ..models.message_push import MessagePush
from ..models.message_push import MessagePushUser from ..models.message_push import MessagePushUser

View File

@ -13,9 +13,10 @@ def files_path(instance, filename):
class SaveFile(CoreModel): class SaveFile(CoreModel):
name = CharField(max_length=128, verbose_name="文件名称", null=True, blank=True) name = CharField(max_length=128, verbose_name="文件名称", null=True, blank=True)
type = CharField(max_length=32, verbose_name="文件类型", null=True, blank=True) type = CharField(max_length=200, verbose_name="文件类型", null=True, blank=True)
size = CharField(max_length=64, verbose_name="文件大小", null=True, blank=True) size = CharField(max_length=64, verbose_name="文件大小", null=True, blank=True)
address = CharField(max_length=16, verbose_name="存储位置", null=True, blank=True) # 本地、阿里云、腾讯云.. address = CharField(max_length=16, verbose_name="存储位置", null=True, blank=True) # 本地、阿里云、腾讯云..
source = CharField(max_length=16, verbose_name="文件来源", null=True, blank=True) # 导出、用户上传.
oss_url = CharField(max_length=200, verbose_name="OSS地址", null=True, blank=True) oss_url = CharField(max_length=200, verbose_name="OSS地址", null=True, blank=True)
status = BooleanField(default=True, verbose_name="文件是否存在") status = BooleanField(default=True, verbose_name="文件是否存在")
file = FileField(verbose_name="文件URL", upload_to=files_path, ) file = FileField(verbose_name="文件URL", upload_to=files_path, )

View File

@ -1,19 +0,0 @@
from django.db.models import TextField, CharField
from ...op_drf.models import CoreModel
class WebSet(CoreModel):
name = CharField(max_length=64, verbose_name="站点名称")
web_site = CharField(max_length=256, verbose_name="站点网址", null=True, blank=True)
logo = CharField(max_length=256, verbose_name="网站Logo", null=True, blank=True)
record_info = TextField(verbose_name="备案信息", null=True, blank=True)
statistics_code = TextField(verbose_name="统计代码", null=True, blank=True)
copyright_info = TextField(verbose_name="版权信息", null=True, blank=True)
class Meta:
verbose_name = '站点设置'
verbose_name_plural = verbose_name
def __str__(self):
return f"{self.name}"

View File

@ -1,3 +1,4 @@
from django.core.cache import cache
from rest_framework import serializers from rest_framework import serializers
from .models import LoginInfor, OperationLog, CeleryLog from .models import LoginInfor, OperationLog, CeleryLog
@ -79,6 +80,10 @@ class DictDetailsCreateUpdateSerializer(CustomModelSerializer):
字典详情 创建/更新时的列化器 字典详情 创建/更新时的列化器
""" """
def save(self, **kwargs):
cache.delete('system_dict_details')
return super().save(**kwargs)
class Meta: class Meta:
model = DictDetails model = DictDetails
fields = '__all__' fields = '__all__'
@ -114,13 +119,17 @@ class ConfigSettingsCreateUpdateSerializer(CustomModelSerializer):
参数设置 创建/更新时的列化器 参数设置 创建/更新时的列化器
""" """
def save(self, **kwargs):
cache.delete('system_configKey')
return super().save(**kwargs)
class Meta: class Meta:
model = ConfigSettings model = ConfigSettings
fields = '__all__' fields = '__all__'
# ================================================= # # ================================================= #
# ************** 参数设置 序列化器 ************** # # ************** 文件管理 序列化器 ************** #
# ================================================= # # ================================================= #
class SaveFileSerializer(CustomModelSerializer): class SaveFileSerializer(CustomModelSerializer):
@ -136,7 +145,7 @@ class SaveFileSerializer(CustomModelSerializer):
class SaveFileCreateUpdateSerializer(CustomModelSerializer): class SaveFileCreateUpdateSerializer(CustomModelSerializer):
""" """
字典详情 创建/更新时的列化器 文件管理 创建/更新时的列化器
""" """
file_url = serializers.CharField(source='file.url', read_only=True) file_url = serializers.CharField(source='file.url', read_only=True)
@ -146,6 +155,7 @@ class SaveFileCreateUpdateSerializer(CustomModelSerializer):
self.validated_data['size'] = files.size self.validated_data['size'] = files.size
self.validated_data['type'] = files.content_type self.validated_data['type'] = files.content_type
self.validated_data['address'] = '本地存储' self.validated_data['address'] = '本地存储'
self.validated_data['source'] = '用户上传'
instance = super().save(**kwargs) instance = super().save(**kwargs)
# 进行判断是否需要OSS上传 # 进行判断是否需要OSS上传
return instance return instance
@ -228,7 +238,6 @@ class LoginInforSerializer(CustomModelSerializer):
""" """
登录日志 简单序列化器 登录日志 简单序列化器
""" """
creator_name = serializers.SlugRelatedField(slug_field="username", source="creator", read_only=True)
class Meta: class Meta:
model = LoginInfor model = LoginInfor
@ -239,7 +248,6 @@ class ExportLoginInforSerializer(CustomModelSerializer):
""" """
导出 登录日志 简单序列化器 导出 登录日志 简单序列化器
""" """
creator_name = serializers.SlugRelatedField(slug_field="username", source="creator", read_only=True)
class Meta: class Meta:
model = LoginInfor model = LoginInfor
@ -255,7 +263,6 @@ class OperationLogSerializer(CustomModelSerializer):
""" """
操作日志 简单序列化器 操作日志 简单序列化器
""" """
creator_name = serializers.SlugRelatedField(slug_field="username", source="creator", read_only=True)
class Meta: class Meta:
model = OperationLog model = OperationLog
@ -266,7 +273,6 @@ class ExportOperationLogSerializer(CustomModelSerializer):
""" """
导出 操作日志 简单序列化器 导出 操作日志 简单序列化器
""" """
creator_name = serializers.SlugRelatedField(slug_field="username", source="creator", read_only=True)
class Meta: class Meta:
model = OperationLog model = OperationLog

View File

@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
from ..system.views import DictDataModelViewSet, DictDetailsModelViewSet, \ from ..system.views import DictDataModelViewSet, DictDetailsModelViewSet, \
ConfigSettingsModelViewSet, SaveFileModelViewSet, MessagePushModelViewSet, LoginInforModelViewSet, \ ConfigSettingsModelViewSet, SaveFileModelViewSet, MessagePushModelViewSet, LoginInforModelViewSet, \
OperationLogModelViewSet, CeleryLogModelViewSet OperationLogModelViewSet, CeleryLogModelViewSet, SystemInfoApiView
router = DefaultRouter() router = DefaultRouter()
router.register(r'dict/type', DictDataModelViewSet) router.register(r'dict/type', DictDataModelViewSet)
@ -20,10 +20,14 @@ urlpatterns = [
re_path('config/configKey/(?P<pk>.*)/', ConfigSettingsModelViewSet.as_view({'get': 'get_config_key'})), re_path('config/configKey/(?P<pk>.*)/', ConfigSettingsModelViewSet.as_view({'get': 'get_config_key'})),
# 参数管理导出 # 参数管理导出
re_path('config/export/', ConfigSettingsModelViewSet.as_view({'get': 'export'})), re_path('config/export/', ConfigSettingsModelViewSet.as_view({'get': 'export'})),
# 清理参数缓存
re_path('config/clearCache/', ConfigSettingsModelViewSet.as_view({'delete': 'clearCache', })),
# 导出字典管理数据 # 导出字典管理数据
re_path('dict/type/export/', DictDataModelViewSet.as_view({'get': 'export'})), re_path('dict/type/export/', DictDataModelViewSet.as_view({'get': 'export'})),
# 导出字典详情数据 # 导出字典详情数据
re_path('dict/data/export/', DictDetailsModelViewSet.as_view({'get': 'export'})), re_path('dict/data/export/', DictDetailsModelViewSet.as_view({'get': 'export'})),
# 清理字典缓存
re_path('dict/type/clearCache/', DictDetailsModelViewSet.as_view({'delete': 'clearCache', })),
# 消息通知导出 # 消息通知导出
re_path('message/export/', MessagePushModelViewSet.as_view({'get': 'export', })), re_path('message/export/', MessagePushModelViewSet.as_view({'get': 'export', })),
# 用户个人消息列表 # 用户个人消息列表
@ -42,5 +46,9 @@ urlpatterns = [
re_path('celery_log/clean/', CeleryLogModelViewSet.as_view({'delete': 'clean_all', })), re_path('celery_log/clean/', CeleryLogModelViewSet.as_view({'delete': 'clean_all', })),
# 导出定时日志 # 导出定时日志
re_path('celery_log/export/', CeleryLogModelViewSet.as_view({'get': 'export', })), re_path('celery_log/export/', CeleryLogModelViewSet.as_view({'get': 'export', })),
# 清除废弃文件
re_path('clearsavefile/', SaveFileModelViewSet.as_view({'post': 'clearsavefile', })),
# 获取系统信息cpu、内存、硬盘
re_path('sys/info/', SystemInfoApiView.as_view())
] ]
urlpatterns += router.urls urlpatterns += router.urls

View File

@ -1,22 +1,30 @@
import os
from django.conf import settings
from django.core.cache import cache
from django.db.models import Q from django.db.models import Q
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.views import APIView
from .models import LoginInfor, OperationLog, CeleryLog from .models import LoginInfor, OperationLog, CeleryLog
from ..op_drf.filters import DataLevelPermissionsFilter from ..op_drf.filters import DataLevelPermissionsFilter
from ..op_drf.viewsets import CustomModelViewSet from ..op_drf.viewsets import CustomModelViewSet
from ..permission.permissions import CommonPermission
from ..system.filters import DictDetailsFilter, DictDataFilter, ConfigSettingsFilter, MessagePushFilter, \ from ..system.filters import DictDetailsFilter, DictDataFilter, ConfigSettingsFilter, MessagePushFilter, \
SaveFileFilter, LoginInforFilter, OperationLogFilter, CeleryLogFilter SaveFileFilter, LoginInforFilter, OperationLogFilter, CeleryLogFilter
from ..system.models import DictData, DictDetails, ConfigSettings, SaveFile, MessagePush from ..system.models import DictData, DictDetails, ConfigSettings, SaveFile, MessagePush
from ..system.models import MessagePushUser from ..system.models import MessagePushUser
from ..system.serializers import DictDataSerializer, DictDataCreateUpdateSerializer, DictDetailsSerializer, \ from ..system.serializers import DictDataSerializer, DictDataCreateUpdateSerializer, DictDetailsSerializer, \
DictDetailsCreateUpdateSerializer, DictDetailsListSerializer, ConfigSettingsSerializer, \ DictDetailsCreateUpdateSerializer, ConfigSettingsSerializer, \
ConfigSettingsCreateUpdateSerializer, SaveFileSerializer, SaveFileCreateUpdateSerializer, \ ConfigSettingsCreateUpdateSerializer, SaveFileSerializer, SaveFileCreateUpdateSerializer, \
ExportConfigSettingsSerializer, ExportDictDataSerializer, ExportDictDetailsSerializer, \ ExportConfigSettingsSerializer, ExportDictDataSerializer, ExportDictDetailsSerializer, \
MessagePushSerializer, MessagePushCreateUpdateSerializer, ExportMessagePushSerializer, LoginInforSerializer, \ MessagePushSerializer, MessagePushCreateUpdateSerializer, ExportMessagePushSerializer, LoginInforSerializer, \
OperationLogSerializer, ExportOperationLogSerializer, ExportLoginInforSerializer, CeleryLogSerializer, \ OperationLogSerializer, ExportOperationLogSerializer, ExportLoginInforSerializer, CeleryLogSerializer, \
ExportCeleryLogSerializer ExportCeleryLogSerializer
from ..utils.export_excel import export_excel_save_model from ..utils.export_excel import export_excel_save_model
from ..utils.file_util import get_all_files, remove_empty_dir, delete_files
from ..utils.response import SuccessResponse from ..utils.response import SuccessResponse
from ..utils.system_info_utils import get_memory_used_percent, get_cpu_used_percent, get_disk_used_percent
class DictDataModelViewSet(CustomModelViewSet): class DictDataModelViewSet(CustomModelViewSet):
@ -31,23 +39,13 @@ class DictDataModelViewSet(CustomModelViewSet):
# retrieve_serializer_class = DetailRoleSerializer # retrieve_serializer_class = DetailRoleSerializer
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
filter_class = DictDataFilter filter_class = DictDataFilter
# update_extra_permission_classes = (IsManagerPermission,) update_extra_permission_classes = (CommonPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,) destroy_extra_permission_classes = (CommonPermission,)
# create_extra_permission_classes = (IsManagerPermission,) create_extra_permission_classes = (CommonPermission,)
search_fields = ('dictName',) search_fields = ('dictName',)
ordering = 'id' # 默认排序 ordering = 'id' # 默认排序
export_field_data = ['字典主键', '字典名称', '字典类型', '字典状态', '创建者', '修改者', '备注']
def export(self, request: Request, *args, **kwargs): export_serializer_class = ExportDictDataSerializer
"""
导出字典管理数据
:param request:
:param args:
:param kwargs:
:return:
"""
field_data = ['字典主键', '字典名称', '字典类型', '字典状态', '创建者', '修改者', '备注']
data = ExportDictDataSerializer(DictData.objects.all(), many=True).data
return SuccessResponse(export_excel_save_model(request, field_data, data, '导出参数管理数据.xls'))
class DictDetailsModelViewSet(CustomModelViewSet): class DictDetailsModelViewSet(CustomModelViewSet):
@ -60,9 +58,9 @@ class DictDetailsModelViewSet(CustomModelViewSet):
update_serializer_class = DictDetailsCreateUpdateSerializer update_serializer_class = DictDetailsCreateUpdateSerializer
filter_class = DictDetailsFilter filter_class = DictDetailsFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
# update_extra_permission_classes = (IsManagerPermission,) update_extra_permission_classes = (CommonPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,) destroy_extra_permission_classes = (CommonPermission,)
# create_extra_permission_classes = (IsManagerPermission,) create_extra_permission_classes = (CommonPermission,)
search_fields = ('dictLabel',) search_fields = ('dictLabel',)
ordering = 'sort' # 默认排序 ordering = 'sort' # 默认排序
@ -74,11 +72,30 @@ class DictDetailsModelViewSet(CustomModelViewSet):
:param kwargs: :param kwargs:
:return: :return:
""" """
queryset = self.queryset.filter(dict_data__dictType=kwargs.get('pk')).order_by('sort') dict_details_dic = cache.get('system_dict_details', {})
if hasattr(self, 'handle_logging'): if not dict_details_dic:
self.handle_logging(request, *args, **kwargs) queryset = self.filter_queryset(self.get_queryset())
serializer = DictDetailsListSerializer(queryset, many=True) queryset_dic = queryset.order_by('sort').values('dict_data__dictType', 'dictLabel', 'dictValue',
return SuccessResponse(serializer.data) 'is_default')
for ele in queryset_dic:
dictType = ele.pop('dict_data__dictType')
if dictType in dict_details_dic:
dict_details_dic[dictType].append(ele)
else:
dict_details_dic[dictType] = [ele]
cache.set('system_dict_details', dict_details_dic, 84600)
return SuccessResponse(dict_details_dic.get(kwargs.get('pk'), []))
def clearCache(self, request: Request, *args, **kwargs):
"""
清理键值缓存
:param request:
:param args:
:param kwargs:
:return:
"""
cache.delete('system_dict_details')
return SuccessResponse(msg='清理成功!')
def export(self, request: Request, *args, **kwargs): def export(self, request: Request, *args, **kwargs):
""" """
@ -103,12 +120,14 @@ class ConfigSettingsModelViewSet(CustomModelViewSet):
create_serializer_class = ConfigSettingsCreateUpdateSerializer create_serializer_class = ConfigSettingsCreateUpdateSerializer
update_serializer_class = ConfigSettingsCreateUpdateSerializer update_serializer_class = ConfigSettingsCreateUpdateSerializer
filter_class = ConfigSettingsFilter filter_class = ConfigSettingsFilter
extra_filter_backends = [DataLevelPermissionsFilter]
# update_extra_permission_classes = (IsManagerPermission,)
# destroy_extra_permission_classes = (IsManagerPermission,)
# create_extra_permission_classes = (IsManagerPermission,)
search_fields = ('configName',) search_fields = ('configName',)
ordering = 'id' # 默认排序 ordering = 'id' # 默认排序
extra_filter_backends = [DataLevelPermissionsFilter]
update_extra_permission_classes = (CommonPermission,)
destroy_extra_permission_classes = (CommonPermission,)
create_extra_permission_classes = (CommonPermission,)
export_field_data = ['参数主键', '参数名称', '参数键名', '参数键值', '系统内置', '参数状态', '创建者', '修改者', '备注']
export_serializer_class = ExportConfigSettingsSerializer
def get_config_key(self, request: Request, *args, **kwargs): def get_config_key(self, request: Request, *args, **kwargs):
""" """
@ -118,22 +137,24 @@ class ConfigSettingsModelViewSet(CustomModelViewSet):
:param kwargs: :param kwargs:
:return: :return:
""" """
queryset = self.queryset.filter(configKey=kwargs.get('pk')).first() config_key_dic = cache.get('system_configKey')
# if hasattr(self, 'handle_logging'): if not config_key_dic:
# self.handle_logging(request, *args, **kwargs) queryset = self.filter_queryset(self.get_queryset())
return SuccessResponse(msg=queryset.configValue if queryset else '') config_key_dic = {ele.get('configKey'): ele.get('configValue') for ele in
queryset.values('configValue', 'configKey')}
cache.set('system_configKey', config_key_dic, 84600)
return SuccessResponse(msg=config_key_dic.get(kwargs.get('pk'), ''))
def export(self, request: Request, *args, **kwargs): def clearCache(self, request: Request, *args, **kwargs):
""" """
导出参数管理数据 清理键值缓存
:param request: :param request:
:param args: :param args:
:param kwargs: :param kwargs:
:return: :return:
""" """
field_data = ['参数主键', '参数名称', '参数键名', '参数键值', '系统内置', '参数状态', '创建者', '修改者', '备注'] cache.delete('system_configKey')
data = ExportConfigSettingsSerializer(ConfigSettings.objects.all(), many=True).data return SuccessResponse(msg='清理成功!')
return SuccessResponse(export_excel_save_model(request, field_data, data, '导出参数管理数据.xls'))
class SaveFileModelViewSet(CustomModelViewSet): class SaveFileModelViewSet(CustomModelViewSet):
@ -146,9 +167,32 @@ class SaveFileModelViewSet(CustomModelViewSet):
update_serializer_class = SaveFileCreateUpdateSerializer update_serializer_class = SaveFileCreateUpdateSerializer
filter_class = SaveFileFilter filter_class = SaveFileFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
update_extra_permission_classes = (CommonPermission,)
destroy_extra_permission_classes = (CommonPermission,)
create_extra_permission_classes = (CommonPermission,)
search_fields = ('configName',) search_fields = ('configName',)
ordering = '-create_datetime' # 默认排序 ordering = '-create_datetime' # 默认排序
def clearsavefile(self, request: Request, *args, **kwargs):
"""
清理废弃文件
:param request:
:param args:
:param kwargs:
:return:
"""
# 获取废弃文件列表
file_list = get_all_files(os.path.join(settings.MEDIA_ROOT, 'system'))
queryset_files = [os.path.join(os.path.join(settings.MEDIA_ROOT) + os.sep, ele) for ele in
list(self.get_queryset().values_list('file', flat=True))]
delete_list = list(set(file_list) - set(queryset_files))
# 进行文件删除操作
delete_files(delete_list)
# 递归删除空文件
remove_empty_dir(os.path.join(settings.MEDIA_ROOT, 'system'))
return SuccessResponse(msg=f"成功清理废弃文件{len(delete_list)}")
class MessagePushModelViewSet(CustomModelViewSet): class MessagePushModelViewSet(CustomModelViewSet):
""" """
@ -158,23 +202,31 @@ class MessagePushModelViewSet(CustomModelViewSet):
serializer_class = MessagePushSerializer serializer_class = MessagePushSerializer
create_serializer_class = MessagePushCreateUpdateSerializer create_serializer_class = MessagePushCreateUpdateSerializer
update_serializer_class = MessagePushCreateUpdateSerializer update_serializer_class = MessagePushCreateUpdateSerializer
extra_filter_backends = [DataLevelPermissionsFilter] # extra_filter_backends = [DataLevelPermissionsFilter]
update_extra_permission_classes = (CommonPermission,)
destroy_extra_permission_classes = (CommonPermission,)
create_extra_permission_classes = (CommonPermission,)
filter_class = MessagePushFilter filter_class = MessagePushFilter
ordering = "-update_datetime" # 默认排序 ordering = "-update_datetime" # 默认排序
export_field_data = ['消息序号', '标题', '内容', '消息类型', '是否审核', '消息状态', '通知接收消息用户',
'创建者', '修改者', '修改时间', '创建时间']
export_serializer_class = ExportMessagePushSerializer
def get_user_messages(self, request: Request, *args, **kwargs): def get_user_messages(self, request: Request, *args, **kwargs):
""" """
获取用户自己消息列表 获取用户自己消息列表
""" """
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
queryset = queryset.filter(status=2)
is_read = request.query_params.get('is_read', None) is_read = request.query_params.get('is_read', None)
if is_read: if is_read:
if is_read == 'False': if is_read == 'False':
queryset = queryset.filter(Q(messagepushuser_message_push__is_read=is_read) | Q(user=None)) queryset = queryset.exclude(Q(messagepushuser_message_push__is_read=True),
else: Q(messagepushuser_message_push__user=request.user))
queryset = queryset.filter(messagepushuser_message_push__is_read=is_read) elif is_read == 'True':
queryset = queryset.filter(messagepushuser_message_push__is_read=True,
queryset = queryset.filter(is_reviewed=True) messagepushuser_message_push__user=request.user)
queryset = queryset.filter(is_reviewed=True).distinct()
page = self.paginate_queryset(queryset) page = self.paginate_queryset(queryset)
if hasattr(self, 'handle_logging'): if hasattr(self, 'handle_logging'):
self.handle_logging(request, *args, **kwargs) self.handle_logging(request, *args, **kwargs)
@ -197,18 +249,6 @@ class MessagePushModelViewSet(CustomModelViewSet):
instance.save() instance.save()
return SuccessResponse() return SuccessResponse()
def export(self, request: Request, *args, **kwargs):
"""
导出岗位
:param request:
:param args:
:param kwargs:
:return:
"""
field_data = ['消息序号', '标题', '内容', '消息类型', '是否审核', '消息状态', '通知接收消息用户', '创建者', '修改者', '修改时间', '创建时间']
data = ExportMessagePushSerializer(MessagePush.objects.all(), many=True).data
return SuccessResponse(export_excel_save_model(request, field_data, data, '导出岗位数据.xls'))
class LoginInforModelViewSet(CustomModelViewSet): class LoginInforModelViewSet(CustomModelViewSet):
""" """
@ -218,6 +258,9 @@ class LoginInforModelViewSet(CustomModelViewSet):
serializer_class = LoginInforSerializer serializer_class = LoginInforSerializer
filter_class = LoginInforFilter filter_class = LoginInforFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
update_extra_permission_classes = (CommonPermission,)
destroy_extra_permission_classes = (CommonPermission,)
create_extra_permission_classes = (CommonPermission,)
ordering = '-create_datetime' # 默认排序 ordering = '-create_datetime' # 默认排序
export_field_data = ['访问编号', '用户名称', '登录地址', '登录地点', '浏览器', '操作系统', export_field_data = ['访问编号', '用户名称', '登录地址', '登录地点', '浏览器', '操作系统',
'登录状态', '操作信息', '登录日期'] '登录状态', '操作信息', '登录日期']
@ -243,6 +286,9 @@ class OperationLogModelViewSet(CustomModelViewSet):
serializer_class = OperationLogSerializer serializer_class = OperationLogSerializer
filter_class = OperationLogFilter filter_class = OperationLogFilter
extra_filter_backends = [DataLevelPermissionsFilter] extra_filter_backends = [DataLevelPermissionsFilter]
update_extra_permission_classes = (CommonPermission,)
destroy_extra_permission_classes = (CommonPermission,)
create_extra_permission_classes = (CommonPermission,)
ordering = '-create_datetime' # 默认排序 ordering = '-create_datetime' # 默认排序
export_field_data = ['请求模块', '请求地址', '请求参数', '请求方式', '操作说明', '请求ip地址', export_field_data = ['请求模块', '请求地址', '请求参数', '请求方式', '操作说明', '请求ip地址',
'请求浏览器', '响应状态码', '操作地点', '操作系统', '返回信息', '响应状态', '操作用户名'] '请求浏览器', '响应状态码', '操作地点', '操作系统', '返回信息', '响应状态', '操作用户名']
@ -266,6 +312,10 @@ class CeleryLogModelViewSet(CustomModelViewSet):
""" """
queryset = CeleryLog.objects.all() queryset = CeleryLog.objects.all()
serializer_class = CeleryLogSerializer serializer_class = CeleryLogSerializer
extra_filter_backends = [DataLevelPermissionsFilter]
update_extra_permission_classes = (CommonPermission,)
destroy_extra_permission_classes = (CommonPermission,)
create_extra_permission_classes = (CommonPermission,)
filter_class = CeleryLogFilter filter_class = CeleryLogFilter
ordering = '-create_datetime' # 默认排序 ordering = '-create_datetime' # 默认排序
export_field_data = ['任务名称', '执行参数', '执行时间', '运行状态', '任务结果', '创建时间'] export_field_data = ['任务名称', '执行参数', '执行时间', '运行状态', '任务结果', '创建时间']
@ -281,3 +331,21 @@ class CeleryLogModelViewSet(CustomModelViewSet):
""" """
self.get_queryset().delete() self.get_queryset().delete()
return SuccessResponse(msg="清空成功") return SuccessResponse(msg="清空成功")
class SystemInfoApiView(APIView):
"""
系统服务监控视图
"""
def get(self, request, *args, **kwargs):
# 获取内存使用率
memory_used_percent = get_memory_used_percent()
# 获取cpu使用率
cpu_used_percent = get_cpu_used_percent()
# 获取硬盘使用率
disk_used_percent = get_disk_used_percent()
return SuccessResponse(data={"memory_used_percent": memory_used_percent,
"cpu_used_percent": cpu_used_percent,
"disk_used_percent": disk_used_percent
})

View File

@ -22,9 +22,9 @@ from django.urls import re_path, include
from rest_framework.documentation import include_docs_urls from rest_framework.documentation import include_docs_urls
from rest_framework.views import APIView from rest_framework.views import APIView
from .op_drf.response import SuccessResponse
from .permission.views import GetUserProfileView, GetRouters from .permission.views import GetUserProfileView, GetRouters
from .utils.login import LoginView, LogoutView from .utils.login import LoginView, LogoutView
from .utils.response import SuccessResponse
class CaptchaRefresh(APIView): class CaptchaRefresh(APIView):

View File

@ -12,8 +12,8 @@ from django.utils.translation import ugettext as _
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework_jwt.utils import jwt_decode_handler from rest_framework_jwt.utils import jwt_decode_handler
from .decorators import exceptionHandler
from .jwt_util import jwt_get_session_id from .jwt_util import jwt_get_session_id
from ..permission.models.users import UserProfile
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
@ -38,6 +38,8 @@ class OpAuthJwtAuthentication(object):
raise exceptions.AuthenticationFailed(msg) raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError: except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
except UserProfile.DoesNotExist:
raise exceptions.AuthenticationFailed()
username = payload.get('username', None) username = payload.get('username', None)
if not username: if not username:

View File

@ -65,8 +65,6 @@ def op_exception_handler(ex, context):
""" """
msg = '' msg = ''
code = '201' code = '201'
request = context.get('request')
request.session['model_name'] = str(get_verbose_name(view=context.get('view')))
if isinstance(ex, AuthenticationFailed): if isinstance(ex, AuthenticationFailed):
code = 401 code = 401

View File

@ -134,7 +134,9 @@ def export_excel_save_model(request, field_data, data, FilName):
savefile.type = 'application/vnd.ms-excel' savefile.type = 'application/vnd.ms-excel'
savefile.size = os.path.getsize(os.path.join(settings.MEDIA_ROOT, file_rul)) savefile.size = os.path.getsize(os.path.join(settings.MEDIA_ROOT, file_rul))
savefile.address = '本地存储' savefile.address = '本地存储'
savefile.source = '导出'
savefile.creator = request.user savefile.creator = request.user
savefile.dept_belong_id = getattr(request.user, 'dept_id', None)
savefile.modifier = request.user.username savefile.modifier = request.user.username
savefile.save() savefile.save()
return SaveFileSerializer(savefile).data return SaveFileSerializer(savefile).data

View File

@ -0,0 +1,48 @@
"""
封装文件操作:
递归读取所有文件目录形成列表
递归删除空目录
批量删除文件
"""
import os
def get_all_files(targetDir):
"""
递归读取所有文件目录形成列表
:param targetDir:
:return:
"""
files = []
listFiles = os.listdir(targetDir)
for i in range(0, len(listFiles)):
path = os.path.join(targetDir, listFiles[i])
if os.path.isdir(path):
files.extend(get_all_files(path))
elif os.path.isfile(path):
files.append(path)
return files
def remove_empty_dir(path):
"""
递归删除空目录
:param path:
:return:
"""
for root, dirs, files in os.walk(path, topdown=False):
if not files and not dirs:
os.rmdir(root)
def delete_files(delete_list: list):
"""
批量删除文件
:param delete_list:
:return:
"""
for file_path in delete_list:
try:
os.remove(file_path)
except(FileNotFoundError):
pass

View File

@ -8,10 +8,10 @@ from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.core.cache import cache from django.core.cache import cache
from django.urls.resolvers import ResolverMatch from django.urls.resolvers import ResolverMatch
from rest_framework.authentication import BaseAuthentication
from rest_framework.settings import api_settings as drf_settings
from user_agents import parse from user_agents import parse
from apps.vadmin.utils.authentication import OpAuthJwtAuthentication
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -26,18 +26,10 @@ def get_request_user(request, authenticate=True):
user: AbstractBaseUser = getattr(request, 'user', None) user: AbstractBaseUser = getattr(request, 'user', None)
if user and user.is_authenticated: if user and user.is_authenticated:
return user return user
authentication: BaseAuthentication = None try:
for authentication_class in drf_settings.DEFAULT_AUTHENTICATION_CLASSES: user, tokrn = OpAuthJwtAuthentication().authenticate(request)
try: except Exception as e:
authentication = authentication_class() pass
user_auth_tuple = authentication.authenticate(request)
if user_auth_tuple is not None:
user, token = user_auth_tuple
if authenticate:
request.user = user
return user
except Exception:
pass
return user or AnonymousUser() return user or AnonymousUser()
@ -127,9 +119,11 @@ def get_request_canonical_path(request, *args, **kwargs):
for value in resolver_match.args: for value in resolver_match.args:
path = path.replace(f"/{value}", "/{id}") path = path.replace(f"/{value}", "/{id}")
for key, value in resolver_match.kwargs.items(): for key, value in resolver_match.kwargs.items():
path = path.replace(f"/{value}", f"/{{{key}}}")
if key == 'pk': if key == 'pk':
pass path = path.replace(f"/{value}", f"/{{id}}")
continue
path = path.replace(f"/{value}", f"/{{{key}}}")
return path return path
@ -182,7 +176,7 @@ def get_login_location(request, *args, **kwargs):
r = requests.get(apiurl) r = requests.get(apiurl)
content = r.content.decode('GBK') content = r.content.decode('GBK')
location = str(content).replace('\r', '').replace('\n', '')[:64] location = str(content).replace('\r', '').replace('\n', '')[:64]
cache.set(request_ip, location, 8640) cache.set(request_ip, location, 86400)
return location return location
except Exception as e: except Exception as e:
pass pass

View File

@ -1,7 +1,7 @@
""" """
常用的Response以及Django的ResponseDRF的Response 常用的Response以及Django的ResponseDRF的Response
""" """
from django.http.response import DjangoJSONEncoder from django.http.response import DjangoJSONEncoder, JsonResponse
from rest_framework.response import Response from rest_framework.response import Response
@ -56,3 +56,36 @@ class ErrorResponse(Response):
def __str__(self): def __str__(self):
return str(self.std_data) return str(self.std_data)
class SuccessJsonResponse(JsonResponse):
"""
标准JsonResponse, SuccessJsonResponse(data)SuccessJsonResponse(data=data)
(1)仅SuccessResponse无法使用时才能推荐使用SuccessJsonResponse
"""
def __init__(self, data, msg='success', encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs):
std_data = {
"code": 200,
"data": data,
"msg": msg,
"status": 'success'
}
super().__init__(std_data, encoder, safe, json_dumps_params, **kwargs)
class ErrorJsonResponse(JsonResponse):
"""
标准JsonResponse, 仅ErrorResponse无法使用时才能使用ErrorJsonResponse
(1)默认错误码返回2001, 也可以指定其他返回码:ErrorJsonResponse(code=xxx)
"""
def __init__(self, data, msg='error', code=201, encoder=OpDRFJSONEncoder, safe=True, json_dumps_params=None,
**kwargs):
std_data = {
"code": code,
"data": data,
"msg": msg,
"status": 'error'
}
super().__init__(std_data, encoder, safe, json_dumps_params, **kwargs)

View File

@ -4,6 +4,27 @@
import psutil as psutil import psutil as psutil
def get_cpu_info():
"""
获取cpu所有信息
"""
pass
def get_memory_info():
"""
获取内存所有信息
"""
pass
def get_disk_info():
"""
获取硬盘所有信息
"""
pass
def get_cpu_used_percent(): def get_cpu_used_percent():
""" """
获取CPU运行情况 获取CPU运行情况
@ -31,7 +52,5 @@ def get_disk_used_percent():
pass pass
if __name__ == '__main__': if __name__ == '__main__':
get_cpu_used_percent() get_disk_used_percent()

View File

@ -15,13 +15,6 @@ DATABASE_PASSWORD = "q1w2e3r4T%Y^U&MYSQL"
# 数据库名 # 数据库名
DATABASE_NAME = "django-vue-admin" DATABASE_NAME = "django-vue-admin"
# ================================================= #
# ************** mongodb 数据库配置 ************** #
# ================================================= #
MONGO_DATABASE_NAME = 'django-vue-admin'
MONGO_HOST = 'localhost'
MONGO_PORT = 27017
# ================================================= # # ================================================= #
# ************** redis 数据库配置 ************** # # ************** redis 数据库配置 ************** #
# ================================================= # # ================================================= #
@ -29,6 +22,8 @@ REDIS_DB = 1
REDIS_HOST = '127.0.0.1' REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379 REDIS_PORT = 6379
REDIS_PASSWORD = 'q1w2e3r4T%Y^U&' REDIS_PASSWORD = 'q1w2e3r4T%Y^U&'
# celery 定时任务redis 库号
CELERY_DB = 2
# ================================================= # # ================================================= #
# ************** 默认配置 ************** # # ************** 默认配置 ************** #
@ -44,3 +39,5 @@ CAPTCHA_STATE = True
# 操作日志配置 # 操作日志配置
API_LOG_ENABLE = True API_LOG_ENABLE = True
API_LOG_METHODS = ['POST', 'DELETE', 'PUT'] # 'ALL' or ['POST', 'DELETE'] API_LOG_METHODS = ['POST', 'DELETE', 'PUT'] # 'ALL' or ['POST', 'DELETE']
# 接口权限
INTERFACE_PERMISSION = True

View File

@ -1,4 +1,5 @@
asgiref==3.3.1 asgiref==3.3.1
celery==5.0.5
Django==2.2.16 Django==2.2.16
django-cors-headers==3.7.0 django-cors-headers==3.7.0
django_celery_beat==2.2.0 django_celery_beat==2.2.0
@ -25,3 +26,4 @@ xlrd==2.0.1
coreapi==2.3.3 coreapi==2.3.3
user-agents==2.2.0 user-agents==2.2.0
eventlet==0.30.2 eventlet==0.30.2
psutil==5.8.0

View File

@ -1,47 +0,0 @@
module.exports = {
title: 'Django-Vue-Admin',
base: '/',
head: [
[
'link', // 设置 favicon.ico注意图片放在 public 文件夹下
{ rel: 'icon', href: '/logo.png' }
]
],
description: '',
themeConfig: {
logo: '/logo.png',
nav: [
{ text: '在线文档', link: '/document/' },
{ text: '关于我们', link: '/about/' },
{ text: '在线体验', link: 'https://demo.django-vue-admin.com/' },
{ text: 'Gitee', link: 'https://gitee.com/liqianglog/django-vue-admin' },
{ text: 'Github', link: 'https://github.com/liqianglog/django-vue-admin' },
],
sidebar: [
{
title: '文档', // 必要的
// path: '/document/', // 可选的, 标题的跳转链接应为绝对路径且必须存在
collapsable: false, // 可选的, 默认值是 true,
sidebarDepth: 1, // 可选的, 默认值是 1
children: [
'/document/',
'/document/kslj',
'/document/hjbs',
'/document/web',
'/document/server',
'/document/gxrz',
]
},
{
title: '其他',
children: [
'/other/cjwt',
'/other/jzzc',
],
collapsable: false,
sidebarDepth: 1,
initialOpenGroupIndex: -1 // 可选的, 默认值是 0
}
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

View File

@ -1,22 +0,0 @@
---
home: true
heroImage: /logo.png
heroText: Django-Vue-Admin
tagline: 使用django+vue进行极速开发的全栈管理系统
actionText: 快速开始 →
actionLink: /document/
features:
- title: 简单易用
details: 大幅度降低应用层代码难度,让每一个刚开始学习 django 和vue的新手都能快速上手。这将会是你上手学习 django+vue的最佳代码。
- title: 自由拓展
details: 系统底层代码和业务逻辑代码分层清晰,不会发生相互干扰,便于根据自己业务方向进行拓展。
- title: 标准化目录
details: 项目目录分层清晰,项目模式结构清晰,包名语义化,让你更加容易理解目录结构,读懂代码更加方便!
- title: 功能完善
details: 内置完整的权限架构,包括:菜单、角色、用户、字典、参数、监控、代码生成等一系列系统常规模块。
- title: 代码生成器
details: 在线配置表信息生成对应的代码,一键生成模块,包含增删改查/排序/导出/权限控制等操作,编译即可使用。
- title: 完全响应式布局
details: 提供多终端适配:电脑、平板、手机等所有主流设备,提供多种不同风格的皮肤。页面美观,高端大气上档次。
footer: MIT Licensed | Copyright © 2021-2021 django-vue-admin All Rights Reserved
---

View File

@ -1 +0,0 @@
# 关于我们

View File

@ -1 +0,0 @@
# 项目介绍

View File

@ -1 +0,0 @@
# 更新日志

View File

@ -1,98 +0,0 @@
# 环境部署
## 1.前端搭建环境
### 1.1 安装node
## 1. 后端搭建环境
### 1.1 安装Python3.8
### 1.2 安装Reids
sudo apt-get install -y redis-server
### 1.3 安装nginx
sudo apt-get install -y nginx
### 1.1 安装其它软件
sudo apt-get install -y python3-venv pcre pcre-devel pcre-static zlib* gcc openssl openssl-devel libffi-devel
## 2. 创建虚拟环境
### 2.1 进入项目目录 cd gh-baohua-backend
在项目根目录中,复制./conf/env.example.py文件为一份新的到./conf文件夹下并重命名为env.py在env.py中配置数据库信息。
### 2.2 激活虚拟环境
#### 2.2.1 python(python3) -m venv xxxx-venv, (xxxx根据情况定义)
#### 2.2.2 \xxxx-venv\Scripts\activate (window OS)
#### 2.2.3 sudo chmod -R 777 xxxx-venv/* (Linux OS)
#### 2.2.4 source ./gh-baohua-venv/bin/activate (Linux OS)
## 3. 升级pip
sudo python(python3) -m pip install --upgrade pip
## 4. 安装依赖环境
pip install -r requirements.txt
## 5. 执行迁移命令:
python manage.py makemigrations
python manage.py migrate
## 6. 初始化数据
python manage.py init
## 7. 启动项目
python manage.py runserver 8888
## 8. 初始账号:admin 密码:123456
## 9. 搭建正式环境完成上述步骤1-6
### 9.1 配置uwsgi.ini(主要配置项)
[uwsgi]
chdir = /mnt/dvadmin-backend
wsgi-file = /mnt/dvadmin-backend/application/wsgi.py
home = /mnt/dvadmin-backend/leo-baohua-venv
pidfile = /mnt/dvadmin-backend/uwsgi.pid
daemonize = /mnt/dvadmin-backend/uwsgi.log
master = true
processes = 8
socket = 0.0.0.0:7777
module = application.wsgi:application
vacuum = true
### 9.2 Nginx 配置
#### 9.2.1 配置uwsgi
server {
listen 7077;
server_name 192.168.xx.xxx;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:7777;
}
}
#### 9.2.2 配置前端
server {
listen 7078;
server_name 192.168.xx.xxx;
root /mnt/dvadmin-ui/dist;
index index.html index.htm index.nginx-debian.html;
location / {
try_files $uri $uri/ /index.html;
}
}
#### 9.2.3 配置前端接口-env.production
VUE_APP_BASE_API = 'http://192.168.xx.xxx:7077'

View File

@ -1 +0,0 @@
# 快速了解

View File

@ -1 +0,0 @@
# 后台手册

View File

@ -1,29 +0,0 @@
# 前端手册
## 添加菜单页面
- 在views中添加页面如下
![image-20210228004427903](http://pic.pgroom.cn/image-20210228004427903.png)
- 在前端系统中,权限管理——菜单管理——新增
- 例如:一级菜单(目录)
![image-20210228004905398](http://pic.pgroom.cn/image-20210228004905398.png)
- 添加末级菜单
![image-20210228010700514](http://pic.pgroom.cn/image-20210228010700514.png)
- 添加外链跳转菜单(菜单类型目录菜单均可)
![image-20210228005521735](http://pic.pgroom.cn/image-20210228005521735.png)
- 按钮同理
- 给角色添加按钮权限
- 权限管理——角色管理——修改——添加对应按钮——刷新页面即可。
<img src="http://pic.pgroom.cn/image-20210228010835275.png" alt="image-20210228010835275" style="zoom: 33%;" />

View File

@ -1 +0,0 @@
# 常见问题

View File

@ -1 +0,0 @@
# 捐赠支持

View File

@ -1,15 +0,0 @@
{
"name": "django-vue-admin-doc",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
},
"author": "",
"license": "ISC",
"devDependencies": {
"vuepress": "^1.8.2"
}
}

View File

@ -3,7 +3,7 @@ ENV = 'development'
# 若依管理系统/开发环境 # 若依管理系统/开发环境
# VUE_APP_BASE_API = 'https://api.django-vue-admin.com' # VUE_APP_BASE_API = 'https://api.django-vue-admin.com'
VUE_APP_BASE_API = 'http://127.0.0.1:8000' VUE_APP_BASE_API = 'http://127.0.0.1:8888'
# 路由懒加载 # 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true VUE_CLI_BABEL_TRANSPILE_MODULES = true

View File

@ -2,22 +2,22 @@
```bash ```bash
# 克隆项目 # 克隆项目
git clone https://gitee.com/y_project/RuoYi-Vue git clone https://gitee.com/liqianglog/django-vue-admin.git
# 进入项目目录 # 进入项目目录
cd ruoyi-ui cd dvadmin-ui
# 安装依赖 # 安装依赖
npm install
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org npm install --registry=https://registry.npm.taobao.org
# 启动服务 # 启动服务
npm run dev npm run dev
# 浏览器访问 http://localhost:8080
# .env.development 文件中可配置启动端口等参数
``` ```
浏览器访问 http://localhost:80 浏览器访问 http://localhost:8080
## 发布 ## 发布

View File

@ -79,8 +79,8 @@
"vue-template-compiler": "2.6.12" "vue-template-compiler": "2.6.12"
}, },
"engines": { "engines": {
"node": ">=8.9", "node": ">=12.0",
"npm": ">= 3.0.0" "npm": ">= 6.0.0"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@ -3,7 +3,7 @@ import request from '@/utils/request'
// 查询在线用户列表 // 查询在线用户列表
export function list(query) { export function list(query) {
return request({ return request({
url: '/monitor/online/list', url: '/admin/monitor/online/list',
method: 'get', method: 'get',
params: query params: query
}) })
@ -12,7 +12,7 @@ export function list(query) {
// 强退用户 // 强退用户
export function forceLogout(tokenId) { export function forceLogout(tokenId) {
return request({ return request({
url: '/monitor/online/' + tokenId, url: '/admin/monitor/online/' + tokenId,
method: 'delete' method: 'delete'
}) })
} }

View File

@ -28,7 +28,7 @@ export function getDept(deptId) {
// 查询部门下拉树结构 // 查询部门下拉树结构
export function treeselect() { export function treeselect() {
return request({ return request({
url: '/admin/permission/dept/treeselect/', url: '/admin/permission/dept/treeselect/?status=1',
method: 'get' method: 'get'
}) })
} }
@ -36,7 +36,7 @@ export function treeselect() {
// 根据角色ID查询部门树结构 // 根据角色ID查询部门树结构
export function roleDeptTreeselect(roleId) { export function roleDeptTreeselect(roleId) {
return request({ return request({
url: '/admin/permission/dept/roleDeptTreeselect/' + roleId + '/', url: '/admin/permission/dept/roleDeptTreeselect/' + roleId + '/?status=1',
method: 'get' method: 'get'
}) })
} }

View File

@ -20,7 +20,7 @@ export function getMenu(menuId) {
// 查询菜单下拉树结构 // 查询菜单下拉树结构
export function treeselect() { export function treeselect() {
return request({ return request({
url: '/admin/permission/menus/treeselect/', url: '/admin/permission/menus/treeselect/?status=1',
method: 'get' method: 'get'
}) })
} }
@ -28,7 +28,7 @@ export function treeselect() {
// 根据角色ID查询菜单下拉树结构 // 根据角色ID查询菜单下拉树结构
export function roleMenuTreeselect(roleId) { export function roleMenuTreeselect(roleId) {
return request({ return request({
url: '/admin/permission/menus/roleMenuTreeselect/' + roleId + '/', url: '/admin/permission/menus/roleMenuTreeselect/' + roleId + '/?status=1',
method: 'get' method: 'get'
}) })
} }

View File

@ -20,7 +20,7 @@ export function getConfig(configId) {
// 根据参数键名查询参数值 // 根据参数键名查询参数值
export function getConfigKey(configKey) { export function getConfigKey(configKey) {
return request({ return request({
url: '/admin/system/config/configKey/' + configKey + '/', url: '/admin/system/config/configKey/' + configKey + '/?status=1',
method: 'get' method: 'get'
}) })
} }

View File

@ -20,7 +20,7 @@ export function getData(dictCode) {
// 根据字典类型查询字典数据信息 // 根据字典类型查询字典数据信息
export function getDicts(dictType) { export function getDicts(dictType) {
return request({ return request({
url: '/admin/system/dict/get/type/' + dictType + '/', url: '/admin/system/dict/get/type/' + dictType + '/?status=1',
method: 'get' method: 'get'
}) })
} }

View File

@ -39,9 +39,9 @@
</template> </template>
<script> <script>
import { getToken } from "@/utils/auth"; import {getToken} from "@/utils/auth";
export default { export default {
props: { props: {
// //
value: [String, Object, Array], value: [String, Object, Array],
@ -135,8 +135,12 @@ export default {
}, },
// //
handleUploadSuccess(res, file) { handleUploadSuccess(res, file) {
this.$message.success("上传成功"); if (res.code === 200) {
this.$emit("input", res.url); this.$message.success("上传成功");
this.$emit("input", res.data.file);
} else {
this.$message.error(res.msg);
}
}, },
// //
handleDelete(index) { handleDelete(index) {

View File

@ -14,7 +14,7 @@
<div class="right-menu-item hover-effect"> <div class="right-menu-item hover-effect">
<router-link to="/user/msg"> <router-link to="/user/msg">
<i class="el-icon-message-solid badge-item-icon"></i> <i class="el-icon-message-solid badge-item-icon"></i>
<el-badge :value="count" :max="99" style="margin-left: -4px;" v-if="count"> <el-badge :value="unread_msg_count" :max="99" style="margin-left: -4px;" v-if="unread_msg_count">
</el-badge> </el-badge>
</router-link> </router-link>
</div> </div>
@ -81,11 +81,11 @@ export default {
}, },
data() { data() {
return { return {
count: store.getters.unread_msg_count, count: store.unread_msg_count,
}; };
}, },
computed: { computed: {
...mapGetters(["sidebar", "avatar", "device"]), ...mapGetters(["sidebar", "avatar", "device", "unread_msg_count"]),
setting: { setting: {
get() { get() {
return this.$store.state.settings.showSettings; return this.$store.state.settings.showSettings;

View File

@ -23,6 +23,7 @@ import {
parseTime, parseTime,
resetForm, resetForm,
selectDictLabel, selectDictLabel,
selectDictDefault,
selectDictLabels selectDictLabels
} from "@/utils/ruoyi"; } from "@/utils/ruoyi";
import Pagination from "@/components/Pagination"; import Pagination from "@/components/Pagination";
@ -40,6 +41,7 @@ Vue.prototype.parseTime = parseTime
Vue.prototype.resetForm = resetForm Vue.prototype.resetForm = resetForm
Vue.prototype.addDateRange = addDateRange Vue.prototype.addDateRange = addDateRange
Vue.prototype.selectDictLabel = selectDictLabel Vue.prototype.selectDictLabel = selectDictLabel
Vue.prototype.selectDictDefault = selectDictDefault
Vue.prototype.selectDictLabels = selectDictLabels Vue.prototype.selectDictLabels = selectDictLabels
Vue.prototype.getCrontabData = getCrontabData Vue.prototype.getCrontabData = getCrontabData
Vue.prototype.getIntervalData = getIntervalData Vue.prototype.getIntervalData = getIntervalData

View File

@ -7,7 +7,8 @@ const user = {
name: '', name: '',
avatar: '', avatar: '',
roles: [], roles: [],
permissions: [] permissions: [],
unread_msg_count: 0
}, },
mutations: { mutations: {

View File

@ -73,6 +73,20 @@ export function selectDictLabel(datas, value) {
}) })
return actions.join(''); return actions.join('');
} }
// 获取字典默认值
export function selectDictDefault(datas) {
var actions = [];
Object.keys(datas).some((key) => {
if (datas[key].is_default === true) {
actions.push(datas[key].dictValue);
return true;
}
})
if (!actions[0] && datas[0]) {
actions.push(datas[0].dictValue)
}
return actions.join('');
}
// 回显数据字典字符串数组 // 回显数据字典字符串数组
export function selectDictLabels(datas, value, separator) { export function selectDictLabels(datas, value, separator) {

View File

@ -253,7 +253,7 @@ export default {
leader: undefined, leader: undefined,
phone: undefined, phone: undefined,
email: undefined, email: undefined,
status: "0" status: this.selectDictDefault(this.statusOptions),
}; };
this.resetForm("form"); this.resetForm("form");
}, },

View File

@ -401,16 +401,16 @@
name: undefined, name: undefined,
icon: undefined, icon: undefined,
web_path: undefined, web_path: undefined,
menuType: "0", menuType: this.selectDictDefault(this.menuTypeOptions),
orderNum: undefined, orderNum: undefined,
component_path: undefined, component_path: undefined,
interface_path: undefined, interface_path: undefined,
perms: undefined, perms: undefined,
interface_method: 'GET', interface_method: this.selectDictDefault(this.interfaceMethodOptions),
isFrame: "1", isFrame: "1",
isCache: "1", isCache: "1",
visible: "1", visible: this.selectDictDefault(this.visibleOptions),
status: "1" status: this.selectDictDefault(this.statusOptions)
}; };
this.resetForm("form"); this.resetForm("form");
}, },

View File

@ -242,7 +242,7 @@ export default {
postCode: undefined, postCode: undefined,
postName: undefined, postName: undefined,
postSort: 0, postSort: 0,
status: "0", status: this.selectDictDefault(this.statusOptions),
remark: undefined remark: undefined
}; };
this.resetForm("form"); this.resetForm("form");

View File

@ -440,7 +440,7 @@ export default {
roleName: undefined, roleName: undefined,
roleKey: undefined, roleKey: undefined,
roleSort: 0, roleSort: 0,
status: "0", status: this.selectDictDefault(this.statusOptions),
menu: [], menu: [],
dept: [], dept: [],
menuCheckStrictly: true, menuCheckStrictly: true,

View File

@ -275,8 +275,8 @@ export default {
configName: undefined, configName: undefined,
configKey: undefined, configKey: undefined,
configValue: undefined, configValue: undefined,
configType: "Y", configType: this.selectDictDefault(this.typeOptions),
status: '0', status: this.selectDictDefault(this.statusOptions),
remark: undefined remark: undefined
}; };
this.resetForm("form"); this.resetForm("form");

View File

@ -79,6 +79,16 @@
v-hasPermi="['system:dict:type:export:get']" v-hasPermi="['system:dict:type:export:get']"
>导出</el-button> >导出</el-button>
</el-col> </el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-refresh"
size="mini"
@click="handleClearCache"
v-hasPermi="['system:dict:type:clearcache:delete']"
>清理缓存</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
@ -181,7 +191,7 @@
<script> <script>
import {addData, delData, exportData, getData, listData, updateData} from "@/api/vadmin/system/dict/data"; import {addData, delData, exportData, getData, listData, updateData} from "@/api/vadmin/system/dict/data";
import {getType, listType} from "@/api/vadmin/system/dict/type"; import {getType, listType, clearCache} from "@/api/vadmin/system/dict/type";
export default { export default {
name: "Data", name: "Data",
@ -237,7 +247,6 @@
}, },
created() { created() {
const dictId = this.$route.params && this.$route.params.dictId; const dictId = this.$route.params && this.$route.params.dictId;
console.log(11111,this.$route.params)
this.getType(dictId); this.getType(dictId);
this.getTypeList(); this.getTypeList();
this.getDicts("sys_normal_disable").then(response => { this.getDicts("sys_normal_disable").then(response => {
@ -263,7 +272,6 @@
getList() { getList() {
this.loading = true; this.loading = true;
listData(this.queryParams).then(response => { listData(this.queryParams).then(response => {
console.log(1212,response.data.count)
this.dataList = response.data.results; this.dataList = response.data.results;
this.total = response.data.count; this.total = response.data.count;
this.loading = false; this.loading = false;
@ -285,7 +293,7 @@
dictLabel: undefined, dictLabel: undefined,
dictValue: undefined, dictValue: undefined,
sort: 0, sort: 0,
status: "0", status: this.selectDictDefault(this.typeOptions),
remark: undefined remark: undefined
}; };
this.resetForm("form"); this.resetForm("form");
@ -379,6 +387,12 @@
}).then(response => { }).then(response => {
this.download(response.data.file_url,response.data.name); this.download(response.data.file_url,response.data.name);
}) })
},
/** 清理缓存按钮操作 */
handleClearCache() {
clearCache().then(response => {
this.msgSuccess("清理成功");
});
} }
} }
}; };

View File

@ -274,7 +274,7 @@ export default {
id: undefined, id: undefined,
dictName: undefined, dictName: undefined,
dictType: undefined, dictType: undefined,
status: "0", status: this.selectDictDefault(this.statusOptions),
remark: undefined remark: undefined
}; };
this.resetForm("form"); this.resetForm("form");

View File

@ -103,9 +103,11 @@
// //
if (this.badgeType === "danger") { if (this.badgeType === "danger") {
updateIsRead(this.showingMsgItem).then(response => { updateIsRead(this.showingMsgItem).then(response => {
store.getters.unread_msg_count if(response.code === 200){
this.open = false; store.commit('SET_UNREAD_MSG_COUNT', store.getters.unread_msg_count - 1);
this.getList(); this.open = false;
this.getList();
}
}); });
} }
} }

View File

@ -232,9 +232,7 @@
open: false, open: false,
// //
MessagePushTypeOptions: [], MessagePushTypeOptions: [],
// //
StatusOptions: [],
//
MessagePushStatusOptions: [], MessagePushStatusOptions: [],
// //
queryParams: { queryParams: {
@ -307,8 +305,8 @@
content: undefined, content: undefined,
to_path: undefined, to_path: undefined,
is_reviewed: true, is_reviewed: true,
message_type: "1", message_type: this.selectDictDefault(this.MessagePushStatusOptions),
status: '1', status: this.selectDictDefault(this.MessagePushStatusOptions),
}; };
this.resetForm("form"); this.resetForm("form");
}, },

View File

@ -266,9 +266,9 @@ export default {
this.form = { this.form = {
noticeId: undefined, noticeId: undefined,
noticeTitle: undefined, noticeTitle: undefined,
noticeType: undefined, noticeType: this.selectDictDefault(this.typeOptions),
noticeContent: undefined, noticeContent: undefined,
status: "0" status: this.selectDictDefault(this.statusOptions)
}; };
this.resetForm("form"); this.resetForm("form");
}, },

View File

@ -14,7 +14,7 @@
<el-form-item label="文件类型" prop="type"> <el-form-item label="文件类型" prop="type">
<el-input <el-input
v-model="queryParams.type" v-model="queryParams.type"
placeholder="请输入文件类型(待完善)" placeholder="请输入文件类型"
clearable clearable
size="small" size="small"
style="width: 240px" style="width: 240px"
@ -72,8 +72,33 @@
<el-table-column label="文件类型" width="150" align="center" prop="type"/> <el-table-column label="文件类型" width="150" align="center" prop="type"/>
<el-table-column label="文件大小" width="90" align="center" prop="size"/> <el-table-column label="文件大小" width="90" align="center" prop="size"/>
<el-table-column label="存储位置" align="center" prop="address"/> <el-table-column label="存储位置" align="center" prop="address"/>
<el-table-column label="文件本地地址" align="center" prop="file" :show-overflow-tooltip="true"/> <el-table-column label="文件来源" align="center" prop="source"/>
<el-table-column label="OSS地址" align="center" prop="oss_url" :show-overflow-tooltip="true"/> <el-table-column label="文件本地地址" align="center" prop="file" :show-overflow-tooltip="true">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-document-copy"
@click="CopyFileUrl(scope.row.file)"
v-if="scope.row.file"
></el-button>
&nbsp;
<span>{{ scope.row.file }}</span>
</template>
</el-table-column>
<el-table-column label="OSS地址" align="center" prop="oss_url" :show-overflow-tooltip="true">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-document-copy"
@click="CopyFileUrl(scope.row.oss_url)"
v-if="scope.row.oss_url"
></el-button>
&nbsp;
<span>{{ scope.row.oss_url }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="create_datetime" width="160"> <el-table-column label="创建时间" align="center" prop="create_datetime" width="160">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ parseTime(scope.row.create_datetime) }}</span> <span>{{ parseTime(scope.row.create_datetime) }}</span>
@ -218,9 +243,9 @@
type: "warning" type: "warning"
}).then(function () { }).then(function () {
return clearSaveFile(); return clearSaveFile();
}).then(() => { }).then((values) => {
this.getList(); this.getList();
this.msgSuccess("删除成功"); this.msgSuccess(values.msg);
}) })
}, },
/** 完成按钮 */ /** 完成按钮 */
@ -229,6 +254,17 @@
this.open = false; this.open = false;
this.$refs.saveFile.fileList = [] this.$refs.saveFile.fileList = []
}, },
CopyFileUrl(values){
const input = document.createElement('input');
document.body.appendChild(input);
input.setAttribute('value', values);
input.select();
if (document.execCommand('copy')) {
document.execCommand('copy');
this.msgSuccess("复制成功");
}
document.body.removeChild(input);
}
} }
} }