diff --git a/client/plugin-module.js b/client/plugin-module.js
index 3dc2505..ebec237 100644
--- a/client/plugin-module.js
+++ b/client/plugin-module.js
@@ -1 +1 @@
-module.exports = {"import-postman" : {module: require('exts/yapi-plugin-import-postman/client.js'),options: null},"import-har" : {module: require('exts/yapi-plugin-import-har/client.js'),options: null},"advanced-mock" : {module: require('exts/yapi-plugin-advanced-mock/client.js'),options: null},"import-swagger" : {module: require('exts/yapi-plugin-import-swagger/client.js'),options: null},"statistics" : {module: require('exts/yapi-plugin-statistics/client.js'),options: null},"export-data" : {module: require('exts/yapi-plugin-export-data/client.js'),options: null},"gen-services" : {module: require('exts/yapi-plugin-gen-services/client.js'),options: null},"export-swagger2-data" : {module: require('exts/yapi-plugin-export-swagger2-data/client.js'),options: null},"import-yapi-json" : {module: require('exts/yapi-plugin-import-yapi-json/client.js'),options: null},"wiki" : {module: require('exts/yapi-plugin-wiki/client.js'),options: null},"swagger-auto-sync" : {module: require('exts/yapi-plugin-swagger-auto-sync/client.js'),options: null},"export-postman" : {module: require('exts/yapi-plugin-export-postman/client.js'),options: null}}
\ No newline at end of file
+module.exports = {"import-postman" : {module: require('exts/yapi-plugin-import-postman/client.js'),options: null},"import-har" : {module: require('exts/yapi-plugin-import-har/client.js'),options: null},"advanced-mock" : {module: require('exts/yapi-plugin-advanced-mock/client.js'),options: null},"import-swagger" : {module: require('exts/yapi-plugin-import-swagger/client.js'),options: null},"statistics" : {module: require('exts/yapi-plugin-statistics/client.js'),options: null},"export-data" : {module: require('exts/yapi-plugin-export-data/client.js'),options: null},"gen-services" : {module: require('exts/yapi-plugin-gen-services/client.js'),options: null},"export-swagger2-data" : {module: require('exts/yapi-plugin-export-swagger2-data/client.js'),options: null},"import-yapi-json" : {module: require('exts/yapi-plugin-import-yapi-json/client.js'),options: null},"wiki" : {module: require('exts/yapi-plugin-wiki/client.js'),options: null},"swagger-auto-sync" : {module: require('exts/yapi-plugin-swagger-auto-sync/client.js'),options: null},"export-postman" : {module: require('exts/yapi-plugin-export-postman/client.js'),options: null},"export-markdown-template" : {module: require('exts/yapi-plugin-export-markdown-template/client.js'),options: null}}
\ No newline at end of file
diff --git a/common/config.js b/common/config.js
index ce795a7..ad575d7 100644
--- a/common/config.js
+++ b/common/config.js
@@ -23,6 +23,8 @@ module.exports = {
name: 'swagger-auto-sync'
}, {
name: 'export-postman'
+ }, {
+ name: 'export-markdown-template'
}
// {
// name: 'test'
diff --git a/exts/yapi-plugin-export-markdown-template/client.js b/exts/yapi-plugin-export-markdown-template/client.js
new file mode 100644
index 0000000..09604b9
--- /dev/null
+++ b/exts/yapi-plugin-export-markdown-template/client.js
@@ -0,0 +1,23 @@
+import mdTemplate from './mdTemplate/mdTemplate.js'
+
+function exportData(exportDataModule, pid) {
+ exportDataModule.markdown = {
+ name: 'markdown',
+ route: `/api/plugin/exportMarkdown?pid=${pid}`,
+ desc: '根据模板导出项目接口文档'
+ };
+}
+
+
+
+function hander(routers) {
+ routers.mdExport = {
+ name: 'Markdown导出',
+ component: mdTemplate
+ };
+}
+
+module.exports = function() {
+ this.bindHook('export_data', exportData);
+ this.bindHook('sub_setting_nav', hander);
+};
diff --git a/exts/yapi-plugin-export-markdown-template/configModel.js b/exts/yapi-plugin-export-markdown-template/configModel.js
new file mode 100644
index 0000000..5b512d3
--- /dev/null
+++ b/exts/yapi-plugin-export-markdown-template/configModel.js
@@ -0,0 +1,83 @@
+const yapi = require('yapi.js');
+const baseModel = require('models/base.js');
+const mongoose = require('mongoose');
+
+class configModel extends baseModel {
+ getName() {
+ return 'markdown_template_config';
+ }
+
+ getSchema() {
+ return {
+ uid: { type: Number},
+ project_id: { type: Number, required: true },
+ //是否开启自动同步
+ is_export_by_interface: { type: Boolean, default: false },
+ // 模板
+ template_data: String,
+ add_time: Number,
+ up_time: Number,
+ };
+ }
+
+ getByProjectId(id) {
+ return this.model.findOne({
+ project_id: id
+ })
+ }
+
+ delByProjectId(project_id){
+ return this.model.remove({
+ project_id: project_id
+ })
+ }
+
+ save(data) {
+ data.up_time = yapi.commons.time();
+ let m = new this.model(data);
+ return m.save();
+ }
+
+ listAll() {
+ return this.model
+ .find({})
+ .select(
+ '_id uid project_id add_time up_time is_sync_open sync_cron sync_json_url sync_mode old_swagger_content last_sync_time'
+ )
+ .sort({ _id: -1 })
+ .exec();
+ }
+
+ up(data) {
+ console.log(data)
+ let id = data.id;
+ delete data.id;
+ data.up_time = yapi.commons.time();
+ return this.model.update({
+ _id: id
+ }, data)
+ }
+
+ upById(id, data) {
+ delete data.id;
+ data.up_time = yapi.commons.time();
+ return this.model.update({
+ _id: id
+ }, data)
+ }
+
+ del(id){
+ return this.model.remove({
+ _id: id
+ })
+ }
+
+ delByProjectId(projectId){
+ return this.model.remove({
+ project_id: projectId
+ })
+ }
+
+}
+
+module.exports = configModel;
diff --git a/exts/yapi-plugin-export-markdown-template/controller.js b/exts/yapi-plugin-export-markdown-template/controller.js
new file mode 100644
index 0000000..3d3c57c
--- /dev/null
+++ b/exts/yapi-plugin-export-markdown-template/controller.js
@@ -0,0 +1,200 @@
+const baseController = require('controllers/base.js');
+const interfaceModel = require('models/interface.js');
+const projectModel = require('models/project.js');
+const interfaceCatModel = require('models/interfaceCat.js');
+const yapi = require('yapi.js');
+const uuid = require('uuid');
+const configModel = require("./configModel");
+const Safeify = require('safeify').default;
+const JSZip = require("jszip");
+
+class exportMarkdownController extends baseController {
+ constructor(ctx) {
+ super(ctx);
+ this.catModel = yapi.getInst(interfaceCatModel);
+ this.interModel = yapi.getInst(interfaceModel);
+ this.projectModel = yapi.getInst(projectModel);
+ this.configModel = yapi.getInst(configModel);
+ }
+
+ /*
+ handleListClass,handleExistId is same as the exportController(yapi-plugin-export-data).
+ No DRY,but i have no idea to optimize it.
+ */
+
+ async handleListClass(pid, status) {
+ let result = await this.catModel.list(pid),
+ newResult = [];
+ for (let i = 0, item, list; i < result.length; i++) {
+ item = result[i].toObject();
+ list = await this.interModel.listByInterStatus(item._id, status);
+ list = list.sort((a, b) => {
+ return a.index - b.index;
+ });
+ if (list.length > 0) {
+ item.list = list;
+ newResult.push(item);
+ }
+ }
+
+ return newResult;
+ }
+
+ handleExistId(data) {
+ function delArrId(arr, fn) {
+ if (!Array.isArray(arr)) return;
+ arr.forEach(item => {
+ delete item._id;
+ delete item.__v;
+ delete item.uid;
+ delete item.edit_uid;
+ delete item.catid;
+ delete item.project_id;
+
+ if (typeof fn === 'function') fn(item);
+ });
+ }
+
+ delArrId(data, function (item) {
+ delArrId(item.list, function (api) {
+ delArrId(api.req_body_form);
+ delArrId(api.req_params);
+ delArrId(api.req_query);
+ delArrId(api.req_headers);
+ if (api.query_path && typeof api.query_path === 'object') {
+ delArrId(api.query_path.params);
+ }
+ });
+ });
+
+ return data;
+ }
+
+
+
+ async exportData(ctx) {
+ let pid = ctx.request.query.pid;
+ let status = ctx.request.query.status;
+
+ if (!pid) {
+ ctx.body = yapi.commons.resReturn(null, 200, 'pid 不为空');
+ }
+
+ let result = await this.configModel.getByProjectId(pid);
+ // console.log(result);
+ if (!result.is_export_by_interface){
+ console.log("重定向")
+ ctx.status = 302;
+ ctx.redirect(`/api/plugin/export?type=markdown&pid=${pid}`);
+ return ;
+ }
+ let curProject;
+ try {
+ curProject = await this.projectModel.get(pid);
+ // ctx.set('Content-Type', 'application/json');
+ // ctx.set('Content-Type', 'application/octet-stream');
+ const list = await this.handleListClass(pid, status);
+
+ let data = this.handleExistId(list);
+
+ // console.log("curProject", curProject)
+ // console.log("list", list)
+
+
+ let model;
+
+ // const tmpPath = fs.mkdtempSync(path.join(os.tmpdir(),uuid.v4()));
+ // console.log("tmpPath=", tmpPath)
+ const zip = JSZip();
+
+ for (let item of data) {
+ for(let interfaceItem of item.list){
+ const safeVm = new Safeify({
+ timeout: 3000, //超时时间
+ asyncTimeout: 10000, //包含异步操作的超时时间
+ unrestricted: true,
+ quantity: 4, //沙箱进程数量,默认同 CPU 核数
+ memoryQuota: 500, //沙箱最大能使用的内存(单位 m),默认 500m
+ cpuQuota: 0.5, //沙箱的 cpu 资源配额(百分比),默认 50%
+ });
+ // console.log("curProject", curProject)
+ const vmContext = {
+ projectTmp: JSON.stringify(curProject),
+ interfaceTmp: JSON.stringify(interfaceItem),
+ categoryTmp: JSON.stringify(item)
+ };
+ try {
+ safeVm.preset("const project = JSON.parse(projectTmp);const interface = JSON.parse(interfaceTmp);const category=JSON.parse(categoryTmp)")
+ model = await safeVm.run(result.template_data, vmContext);
+ // fs.writeFileSync(`${tmpPath}/${interfaceItem.title}.md`, model.toString())
+ zip.file(`${interfaceItem.title}.md`, model.toString())
+ safeVm.destroy();
+ } catch (e) {
+ console.log(e)
+ return ;
+ }
+ }
+ }
+
+ ctx.set('Content-Disposition', `attachment; filename=${encodeURIComponent(curProject.name)}.zip`);
+ let dataaa = await zip.generateAsync({
+ type: "nodebuffer",
+ // 压缩算法
+ compression: "DEFLATE",
+ streamFiles: true,
+ compressionOptions: {
+ level: 9
+ }
+ });
+ ctx.body = dataaa
+ // ctx.set('Content-Length', dataaa.length);
+
+ // dataaa.copy(ctx.body)
+
+ // fs.unlinkSync(tmpPath)
+ } catch (error) {
+ yapi.commons.log(error, 'error');
+ ctx.body = yapi.commons.resReturn(null, 502, '下载出错');
+ }
+ }
+
+ /**
+ * 保存配置信息
+ * @param {*} ctx
+ */
+ async upConfig(ctx) {
+ let requestBody = ctx.request.body;
+ let projectId = requestBody.project_id;
+ if (!projectId) {
+ return (ctx.body = yapi.commons.resReturn(null, 408, '缺少项目Id'));
+ }
+
+ if ((await this.checkAuth(projectId, 'project', 'edit')) !== true) {
+ return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
+ }
+
+ let result;
+ if (requestBody.id) {
+ result = await this.configModel.up(requestBody);
+ } else {
+ result = await this.configModel.save(requestBody);
+ }
+
+ return (ctx.body = yapi.commons.resReturn(result));
+ }
+
+ /**
+ * 查询配置信息
+ * @param {*} ctx
+ */
+ async getConfig(ctx) {
+ let projectId = ctx.query.project_id;
+ if (!projectId) {
+ return (ctx.body = yapi.commons.resReturn(null, 408, '缺少项目Id'));
+ }
+ let result = await this.configModel.getByProjectId(projectId);
+ return (ctx.body = yapi.commons.resReturn(result));
+ }
+}
+
+module.exports = exportMarkdownController;
diff --git a/exts/yapi-plugin-export-markdown-template/index.js b/exts/yapi-plugin-export-markdown-template/index.js
new file mode 100644
index 0000000..080b4b6
--- /dev/null
+++ b/exts/yapi-plugin-export-markdown-template/index.js
@@ -0,0 +1,4 @@
+module.exports = {
+ server: true,
+ client: true
+ }
\ No newline at end of file
diff --git a/exts/yapi-plugin-export-markdown-template/mdTemplate/mdTemplate.js b/exts/yapi-plugin-export-markdown-template/mdTemplate/mdTemplate.js
new file mode 100644
index 0000000..2a09640
--- /dev/null
+++ b/exts/yapi-plugin-export-markdown-template/mdTemplate/mdTemplate.js
@@ -0,0 +1,172 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import {Form, Switch, Button, message} from 'antd';
+import {handleSwaggerUrlData} from 'client/reducer/modules/project';
+const FormItem = Form.Item;
+import axios from 'axios';
+import AceEditor from "../../../client/components/AceEditor/AceEditor";
+// layout
+const formItemLayout = {
+ labelCol: {
+ lg: { span: 5 },
+ xs: { span: 24 },
+ sm: { span: 10 }
+ },
+ wrapperCol: {
+ lg: { span: 16 },
+ xs: { span: 24 },
+ sm: { span: 12 }
+ },
+ className: 'form-item'
+};
+const tailFormItemLayout = {
+ wrapperCol: {
+ sm: {
+ span: 16,
+ offset: 11
+ }
+ }
+};
+
+@connect(
+ state => {
+ return {
+ projectMsg: state.project.currProject
+ };
+ },
+ {
+ handleSwaggerUrlData
+ }
+)
+@Form.create()
+export default class ProjectInterfaceSync extends Component {
+ static propTypes = {
+ form: PropTypes.object,
+ match: PropTypes.object,
+ projectId: PropTypes.number,
+ projectMsg: PropTypes.object,
+ handleSwaggerUrlData: PropTypes.func
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ config_data: { is_export_by_interface: false, template_data:"",_id:null }
+ };
+ }
+
+ handleSubmit = async () => {
+ const { form, projectId } = this.props;
+ let params = {
+ project_id: projectId,
+ is_export_by_interface: this.state.config_data.is_export_by_interface,
+ template_data: this.state.config_data.template_data,
+ uid: this.props.projectMsg.uid
+ };
+ if (this.state.config_data._id) {
+ params.id = this.state.config_data._id;
+ }
+ if (!this.state.config_data.is_export_by_interface){
+ params.template_data = null;
+ this.setState({
+ config_data: {
+ template_data: null,
+ is_export_by_interface: this.state.config_data.is_export_by_interface
+ }
+ });
+ }
+ form.validateFields(async (err, values) => {
+ if (!err) {
+ let assignValue = Object.assign(params, values);
+ await axios.post('/api/plugin/mdConfig/save', assignValue).then(res => {
+ if (res.data.errcode === 0) {
+ message.success('保存成功');
+ } else {
+ message.error(res.data.errmsg);
+ }
+ });
+ }
+ });
+
+ };
+
+
+ UNSAFE_componentWillMount() {
+ //查询同步任务
+ this.setState({
+ config_data: {}
+ });
+ this.getSyncData();
+ }
+
+ async getSyncData() {
+ let projectId = this.props.projectMsg._id;
+ let result = await axios.get('/api/plugin/mdConfig/get?project_id=' + projectId);
+ if (result.data.errcode === 0) {
+ if (result.data.data) {
+ this.setState({
+ config_data: result.data.data
+ });
+ }
+ }
+ }
+
+ // 是否开启
+ onChange = v => {
+ let config_data = this.state.config_data;
+ config_data.is_export_by_interface = v;
+ this.setState({
+ config_data: config_data
+ });
+ };
+
+
+ handleTemplateInput = e => {
+ let config_data = this.state.config_data;
+ config_data.template_data = e.text;
+ this.setState({
+ config_data: config_data
+ });
+ };
+
+
+ render() {
+ const templateEditor = (
+