-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
08cfa2d
commit f04ea8d
Showing
8 changed files
with
634 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import swaggerAutoSync from './swaggerAutoSync/swaggerAutoSync.js' | ||
|
||
function hander(routers) { | ||
routers.test = { | ||
name: 'Swagger自动同步', | ||
component: swaggerAutoSync | ||
}; | ||
} | ||
|
||
module.exports = function() { | ||
this.bindHook('sub_setting_nav', hander); | ||
}; |
57 changes: 57 additions & 0 deletions
57
exts/yapi-plugin-swagger-auto-sync/controller/syncController.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
const baseController = require('controllers/base.js'); | ||
const yapi = require('yapi.js'); | ||
const syncModel = require('../syncModel.js'); | ||
const projectModel = require('models/project.js'); | ||
const interfaceSyncUtils = require('../interfaceSyncUtils.js') | ||
|
||
class syncController extends baseController { | ||
constructor(ctx) { | ||
super(ctx); | ||
this.syncModel = yapi.getInst(syncModel); | ||
this.projectModel = yapi.getInst(projectModel); | ||
this.interfaceSyncUtils = yapi.getInst(interfaceSyncUtils); | ||
} | ||
|
||
/** | ||
* 保存定时任务 | ||
* @param {*} ctx | ||
*/ | ||
async upSync(ctx) { | ||
let requestBody = ctx.request.body; | ||
let projectId = requestBody.project_id; | ||
if (!projectId) { | ||
return (ctx.body = yapi.commons.resReturn(null, 408, '缺少项目Id')); | ||
} | ||
let result; | ||
if (requestBody.id) { | ||
result = await this.syncModel.up(requestBody); | ||
} else { | ||
result = await this.syncModel.save(requestBody); | ||
} | ||
|
||
//操作定时任务 | ||
if (requestBody.is_sync_open) { | ||
this.interfaceSyncUtils.addSyncJob(projectId, requestBody.sync_cron, requestBody.sync_json_url, requestBody.sync_mode, requestBody.uid); | ||
} else { | ||
this.interfaceSyncUtils.deleteSyncJob(projectId); | ||
} | ||
return (ctx.body = yapi.commons.resReturn(result)); | ||
} | ||
|
||
/** | ||
* 查询定时任务 | ||
* @param {*} ctx | ||
*/ | ||
async getSync(ctx) { | ||
let projectId = ctx.query.project_id; | ||
if (!projectId) { | ||
return (ctx.body = yapi.commons.resReturn(null, 408, '缺少项目Id')); | ||
} | ||
let result = await this.syncModel.getByProjectId(projectId); | ||
return (ctx.body = yapi.commons.resReturn(result)); | ||
} | ||
|
||
} | ||
|
||
|
||
module.exports = syncController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module.exports = { | ||
server: true, | ||
client: true | ||
} |
220 changes: 220 additions & 0 deletions
220
exts/yapi-plugin-swagger-auto-sync/interfaceSyncUtils.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
const schedule = require('node-schedule'); | ||
const openController = require('controllers/open.js'); | ||
const projectModel = require('models/project.js'); | ||
const syncModel = require('./syncModel.js'); | ||
const tokenModel = require('models/token.js'); | ||
const yapi = require('yapi.js') | ||
const sha = require('sha.js'); | ||
const md5 = require('md5'); | ||
const { getToken } = require('utils/token'); | ||
const jobMap = new Map(); | ||
|
||
class syncUtils { | ||
|
||
constructor(ctx) { | ||
yapi.commons.log("-------------------------------------swaggerSyncUtils constructor-----------------------------------------------"); | ||
this.ctx = ctx; | ||
this.openController = yapi.getInst(openController); | ||
this.syncModel = yapi.getInst(syncModel); | ||
this.tokenModel = yapi.getInst(tokenModel) | ||
this.projectModel = yapi.getInst(projectModel); | ||
this.init() | ||
} | ||
|
||
//初始化定时任务 | ||
async init() { | ||
let allSyncJob = await this.syncModel.listAll(); | ||
for (let i = 0, len = allSyncJob.length; i < len; i++) { | ||
let syncItem = allSyncJob[i]; | ||
if (syncItem.is_sync_open) { | ||
this.addSyncJob(syncItem.project_id, syncItem.sync_cron, syncItem.sync_json_url, syncItem.sync_mode, syncItem.uid); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* 新增同步任务. | ||
* @param {*} projectId 项目id | ||
* @param {*} cronExpression cron表达式,针对定时任务 | ||
* @param {*} swaggerUrl 获取swagger的地址 | ||
* @param {*} syncMode 同步模式 | ||
* @param {*} uid 用户id | ||
*/ | ||
async addSyncJob(projectId, cronExpression, swaggerUrl, syncMode, uid) { | ||
let projectToken = await this.getProjectToken(projectId, uid); | ||
//立即执行一次 | ||
this.syncInterface(projectId, swaggerUrl, syncMode, uid, projectToken); | ||
let scheduleItem = schedule.scheduleJob(cronExpression, async () => { | ||
this.syncInterface(projectId, swaggerUrl, syncMode, uid, projectToken); | ||
}); | ||
|
||
//判断是否已经存在这个任务 | ||
let jobItem = jobMap.get(projectId); | ||
if (jobItem) { | ||
jobItem.cancel(); | ||
} | ||
jobMap.set(projectId, scheduleItem); | ||
} | ||
|
||
//同步接口 | ||
async syncInterface(projectId, swaggerUrl, syncMode, uid, projectToken) { | ||
yapi.commons.log('定时器触发, syncJsonUrl:' + swaggerUrl + ",合并模式:" + syncMode); | ||
let oldPorjectData; | ||
try { | ||
oldPorjectData = await this.projectModel.get(projectId); | ||
} catch(e) { | ||
yapi.commons.log('获取项目:' + projectId + '失败'); | ||
this.deleteSyncJob(projectId); | ||
//删除数据库定时任务 | ||
await this.syncModel.delByProjectId(projectId); | ||
return; | ||
} | ||
//如果项目已经删除了 | ||
if (!oldPorjectData) { | ||
yapi.commons.log('项目:' + projectId + '不存在'); | ||
this.deleteSyncJob(projectId); | ||
//删除数据库定时任务 | ||
await this.syncModel.delByProjectId(projectId); | ||
return; | ||
} | ||
let newSwaggerJsonData; | ||
try { | ||
newSwaggerJsonData = await this.getSwaggerContent(swaggerUrl) | ||
if (!newSwaggerJsonData || typeof newSwaggerJsonData !== 'object') { | ||
yapi.commons.log('数据格式出错,请检查') | ||
this.saveSyncLog(0, syncMode, "数据格式出错,请检查", uid, projectId); | ||
} | ||
newSwaggerJsonData = JSON.stringify(newSwaggerJsonData) | ||
} catch (e) { | ||
this.saveSyncLog(0, syncMode, "获取数据失败,请检查", uid, projectId); | ||
yapi.commons.log('获取数据失败' + e.message) | ||
} | ||
|
||
let oldSyncJob = await this.syncModel.getByProjectId(projectId); | ||
|
||
//更新之前判断本次swagger json数据是否跟上次的相同,相同则不更新 | ||
if (oldSyncJob.old_swagger_content && oldSyncJob.old_swagger_content == md5(newSwaggerJsonData)) { | ||
//记录日志 | ||
this.saveSyncLog(0, syncMode, "接口无更新", uid, projectId); | ||
oldSyncJob.last_sync_time = yapi.commons.time(); | ||
await this.syncModel.upById(projectId, oldSyncJob); | ||
return; | ||
} | ||
|
||
let _params = { | ||
type: 'swagger', | ||
json: newSwaggerJsonData, | ||
project_id: projectId, | ||
merge: syncMode, | ||
token: projectToken | ||
} | ||
let requestObj = { | ||
params: _params | ||
}; | ||
await this.openController.importData(requestObj); | ||
|
||
//同步成功就更新同步表的数据 | ||
if (requestObj.body.errcode == 0) { | ||
//修改sync_model的属性 | ||
oldSyncJob.last_sync_time = yapi.commons.time(); | ||
oldSyncJob.old_swagger_content = md5(newSwaggerJsonData); | ||
await this.syncModel.upById(oldSyncJob._id, oldSyncJob); | ||
} | ||
//记录日志 | ||
this.saveSyncLog(requestObj.body.errcode, syncMode, requestObj.body.errmsg, uid, projectId); | ||
} | ||
|
||
getSyncJob(projectId) { | ||
return jobMap.get(projectId); | ||
} | ||
|
||
deleteSyncJob(projectId) { | ||
let jobItem = jobMap.get(projectId); | ||
if (jobItem) { | ||
jobItem.cancel(); | ||
} | ||
} | ||
|
||
/** | ||
* 记录同步日志 | ||
* @param {*} errcode | ||
* @param {*} syncMode | ||
* @param {*} moremsg | ||
* @param {*} uid | ||
* @param {*} projectId | ||
*/ | ||
saveSyncLog(errcode, syncMode, moremsg, uid, projectId) { | ||
yapi.commons.saveLog({ | ||
content: '自动同步接口状态:' + (errcode == 0 ? '成功,' : '失败,') + "合并模式:" + this.getSyncModeName(syncMode) + ",更多信息:" + moremsg, | ||
type: 'project', | ||
uid: uid, | ||
username: "自动同步用户", | ||
typeid: projectId | ||
}); | ||
} | ||
|
||
/** | ||
* 获取项目token,因为导入接口需要鉴权. | ||
* @param {*} project_id 项目id | ||
* @param {*} uid 用户id | ||
*/ | ||
async getProjectToken(project_id, uid) { | ||
try { | ||
let data = await this.tokenModel.get(project_id); | ||
let token; | ||
if (!data) { | ||
let passsalt = yapi.commons.randStr(); | ||
token = sha('sha1') | ||
.update(passsalt) | ||
.digest('hex') | ||
.substr(0, 20); | ||
|
||
await this.tokenModel.save({ project_id, token }); | ||
} else { | ||
token = data.token; | ||
} | ||
|
||
token = getToken(token, uid); | ||
|
||
return token; | ||
} catch (err) { | ||
return ""; | ||
} | ||
} | ||
|
||
getUid(uid) { | ||
return parseInt(uid, 10); | ||
} | ||
|
||
/** | ||
* 转换合并模式的值为中文. | ||
* @param {*} syncMode 合并模式 | ||
*/ | ||
getSyncModeName(syncMode) { | ||
if (syncMode == 'good') { | ||
return '智能合并'; | ||
} else if (syncMode == 'normal') { | ||
return '普通模式'; | ||
} else if (syncMode == 'merge') { | ||
return '完全覆盖'; | ||
} | ||
return ''; | ||
} | ||
|
||
async getSwaggerContent(swaggerUrl) { | ||
const axios = require('axios') | ||
try { | ||
let response = await axios.get(swaggerUrl); | ||
if (response.status > 400) { | ||
throw new Error(`http status "${response.status}"` + '获取数据失败,请确认 swaggerUrl 是否正确') | ||
} | ||
return response.data; | ||
} catch (e) { | ||
let response = e.response; | ||
throw new Error(`http status "${response.status}"` + '获取数据失败,请确认 swaggerUrl 是否正确') | ||
} | ||
} | ||
|
||
} | ||
|
||
module.exports = syncUtils; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const controller = require('./controller/syncController.js'); | ||
const yapi =require('yapi.js'); | ||
const interfaceSyncUtils = require('./interfaceSyncUtils.js'); | ||
|
||
module.exports = function () { | ||
yapi.getInst(interfaceSyncUtils); | ||
|
||
this.bindHook('add_router', function (addRouter) { | ||
addRouter({ | ||
controller: controller, | ||
method: 'get', | ||
path: 'autoSync/get', | ||
action: 'getSync' | ||
}); | ||
addRouter({ | ||
controller: controller, | ||
method: 'post', | ||
path: 'autoSync/save', | ||
action: 'upSync' | ||
}); | ||
}); | ||
|
||
}; |
Oops, something went wrong.