1. 项目分析
为什么要开发这个项目,常年做运维,重复性使用多方工具
现将常用工具整合Jumpserver
+Jenkins
+Zabbix
+Ftp
+Cron
+Ansible
demo
地址
2. 开发框架
django
+ django-restframework
+vue
+ant-design
+jwt
+websocket
+django-channel
+paramiko
+xterm.js
+webshell
+git
+mysql
+redis
+docker
+uwsgi
+nginx
Django3.0 +Vue4.1
3. 初始化后端
3.1 创建项目
安装插件
pip install django
pip install djangorestframework
pip install PymySQL
pip install django-redis
3.2 调整目录
├── docs/ # 项目相关资料保存目录
├── logs/ # 项目运行时/开发时日志目录
├── manage.py
├── luffy/ # 项目主应用,开发时的代码保存
│ ├── apps/ # 开发者的代码保存目录,以模块[子应用]为目录保存
│ ├── libs/ # 第三方类库的保存目录[第三方组件、模块]
│ ├── settings/
│ ├── dev.py # 项目开发时的本地配置
│ ├── prop.py # 项目上线时的运行配置
│ ├── urls.py # 总路由
│ ├── utils/ # 多个模块[子应用]的公共函数类库[自己开发的组件]
└── scripts/ # 保存项目运营时的脚本文件
除了log其他都是以包的形式存在
3.3 配置文件
配置文件的默认设置
from django.conf import global_settings
开发者本地的环境、目录、数据库密码和线上的服务器都会不一样,所以我们的配置文件可以针对不同的环境更改.
3.4 日志配置
官网https://docs.djangoproject.com/zh-hans/3.0/topics/logging/
在settings/dev.py文件中追加如下配置:
# 日志配置
LOGGING = {
'version': 1, #使用的python内置的logging模块,那么python可能会对它进行升级,所以需要写一个版本号,目前就是1版本
'disable_existing_loggers': False, #是否去掉目前项目中其他地方中以及使用的日志功能,但是将来我们可能会引入第三方的模块,里面可能内置了日志功能,所以尽量不要关闭。
'formatters': { #日志记录格式
'verbose': { #levelname等级,asctime记录时间,module表示日志发生的文件名称,lineno行号,message错误信息
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
},
},
'filters': { #过滤器:可以对日志进行输出时的过滤用的
'require_debug_true': { #在debug=True下产生的一些日志信息,要不要记录日志,需要的话就在handlers中加上这个过滤器,不需要就不加
'()': 'django.utils.log.RequireDebugTrue',
},
'require_debug_false': { #和上面相反
'()': 'django.utils.log.RequireDebugFalse',
},
},
'handlers': { #日志处理方式,日志实例
'console': { #在控制台输出时的实例
'level': 'DEBUG', #日志等级;debug是最低等级,那么只要比它高等级的信息都会被记录
'filters': ['require_debug_true'], #在debug=True下才会打印在控制台
'class': 'logging.StreamHandler', #使用的python的logging模块中的StreamHandler来进行输出
'formatter': 'simple'
},
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
# 日志位置,日志文件名,日志保存目录必须手动创建
'filename': os.path.join(os.path.dirname(BASE_DIR), "logs/hippo.log"), #注意,你的文件应该有读写权限。
# 日志文件的最大值,这里我们设置300M
'maxBytes': 300 * 1024 * 1024,
# 日志文件的数量,设置最大日志数量为10
'backupCount': 10,
# 日志格式:详细格式
'formatter': 'verbose',
'encoding': 'utf-8', # 设置默认编码,否则打印出来汉字乱码
},
},
# 日志对象
'loggers': {
'django': { #和django结合起来使用,将django中之前的日志输出内容的时候,按照我们的日志配置进行输出,
'handlers': ['console', 'file'], #将来项目上线,把console去掉
'propagate': True, #冒泡:是否将日志信息记录冒泡给其他的日志处理系统,工作中都是True,不然django这个日志系统捕获到日志信息之后,其他模块中可能也有日志记录功能的模块,就获取不到这个日志信息了
},
}
}
3.5 异常处理
新建utils/exceptions.py
from rest_framework.views import exception_handler
from django.db import DatabaseError
#from redis.exceptions import RedisError
from rest_framework.response import Response
from rest_framework import status
import logging
logger = logging.getLogger('django')
def custom_exception_handler(exc, context):
"""
自定义异常处理
:param exc: 异常类
:param context: 抛出异常的上下文
:return: Response响应对象
"""
# 调用drf框架原生的异常处理方法
response = exception_handler(exc, context)
if response is None:
view = context['view']
if isinstance(exc, DatabaseError):
# 数据库异常
logger.error('[%s] %s' % (view, exc))
response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
return response
# if isinstance(exc, RedisError):
# # redis异常
# logger.error('[%s]%s' % (view, exc))
# response= Response({"message": "redis数据库异常"}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
# return response
settings.py配置文件中添加
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'pippo_api.utils.exceptions.custom_exception_handler',
}
3.6 数据库连接
打开settings/dev.py文件,并配置
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"HOST": "47.95.38.42",
"PORT": 3306,
"USER": "root",
"PASSWORD": "Zxy521",
"NAME": "pippo",
}
}
CACHES = {
# 默认缓存
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# 项目上线时,需要调整这里的路径
"LOCATION": "redis://47.95.38.42:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "zxy52"
}
}
}
在项目主模块的 __init__.py
中导入pymysql
import pymysql
pymysql.version_info = (1, 4, 13, "final", 0)
pymysql.install_as_MySQLdb()
3.7 跨域CORS
通过浏览器访问drf项目,会出现以下错误信息
可以通过settings的ALLOWED_HOSTS,设置允许访问
# 设置哪些客户端可以通过地址访问到后端
ALLOWED_HOSTS = [
'*',
]
官方文档:https://github.com/ottoyiu/django-cors-headers/
pip install django-cors-headers
添加应用
INSTALLED_APPS = (
...
'rest_framework',
'corsheaders',
...
)
中间层设置【必须写在第一个位置】
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
]
添加白名单
# CORS组的配置信息
CORS_ALLOWED_ORIGINS = [
"http://127.0.0.1:8080", #前端地址,可域名,可ip:port
]
CORS_ALLOW_CREDENTIALS = False # 不允许ajax跨域请求时携带cookie,使用jwt之后不需要cookie,将其关闭防止被攻击
至此 Django
初始化环境已完成!
4. 初始化前端
安装vue参考本人博客园 vue篇
4.1 创建项目目录
npm install -g @vue/cli --registry=https://registry.npm.taobao.org
vue create hippo
4.2 初始化前端项目
清除默认的HelloWorld
组件,views中的文件和APP.vue
中的默认样式
<template>
<div id="app">
<router-view/>
</div>
</template>
<style lang="scss">
</style>
routers目录下index.js路由文件中,编写初始化路由对象的代码 .
import Vue from 'vue'
import VueRouter from 'vue-router'
// import Home from '../views/Home.vue' 这里导入可以让用户访问的组件
Vue.use(VueRouter)
// 路由列表
const routes = [
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
由于路由列表为空,现在是空白页面
路由列表的基本配置
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
//路由级别的代码分割
//这会为该路由生成一个单独的块(about.[hash].js)
//当路由被访问时延迟加载。
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
4.3 全局后端地址
在src
目录下创建settings.js
站点开发配置文件:
export default {
Host:"http://127.0.0.1:8000",
}
在main.js
中引入
import settings from './settings'
Vue.prototype.$settings = settings;
4.4 Ant Design
导入方式分全局导入和按需导入
官方文档 https://www.antdv.com/docs/vue/use-with-vue-cli-cn/
npm i ant-design-vue -S --registry=https://registry.npm.taobao.org
生产环境尽量按需求导入,这里选择全部导入main.js
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
Vue.use(Antd);
4.5 Echart
官方文档 https://echarts.apache.org/
npm i echarts -S --registry=https://registry.npm.taobao.org
在main.js
中配置
let echarts = require('echarts')
Vue.prototype.$echarts = echarts
4.6 测试UI框架
新建ShowCenter.vue
<template>
<div class="show_center">
<h1>展示中心页面</h1>
<a-button type="primary">
Primary
</a-button>
<!-- ref是vue找标签使用的-->
<div ref="chart" style="height: 200px; 200px;">
</div>
</div>
</template>
<script>
export default {
name: "ShowCenter",
mounted() {
let myChart = this.$echarts.init(this.$refs.chart);
myChart.setOption({
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
});
}
}
</script>
<style scoped>
</style>
在路由的index.js
添加
const routes = [
{
path: '/show',
name: 'ShowCenter',
component: ShowCenter
},
]
4.7 Axios
官方文档 http://www.axios-js.com/zh-cn/docs/vue-axios.html
npm install --save axios vue-axios --registry=https://registry.npm.taobao.org
在main.js中引用 axios插件
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
发送方式
this.axios.get(api).then((response) => {
console.log(response.data)
})
4.8 重置CSS
在public中新建reset.css
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike,
strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody,
tfoot, thead, tr, th, td ,textarea,input { margin:0; padding:0; }
address,cite,dfn,em,var, i {font-style:normal;}
body {font-size: 16px; line-height: 1.5; color: #222; background: #eee; }
table { border-collapse:collapse; border-spacing:0; }
h1, h2, h3, h4, h5, h6, th { font-size: 100%; font-weight: normal; }
button,input,select,textarea{font-size:100%;}
fieldset,img{border:0;}
a { text-decoration: none; color: #666; background: none }
ul, ol { list-style: none; }
:focus{ outline:none;}
.clearfix{ clear: both; content: ""; display: block; overflow: hidden }
.clear{clear: both;}
.fl{ float: left; }
.fr{float: right;}
在main.js中引入
import '../public/reset.css'