mock平台

project.js 33KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. const projectModel = require('../models/project.js');
  2. const yapi = require('../yapi.js');
  3. const _ = require('underscore');
  4. const baseController = require('./base.js');
  5. const interfaceModel = require('../models/interface.js');
  6. const interfaceColModel = require('../models/interfaceCol.js');
  7. const interfaceCaseModel = require('../models/interfaceCase.js');
  8. const interfaceCatModel = require('../models/interfaceCat.js');
  9. const groupModel = require('../models/group');
  10. const commons = require('../utils/commons.js');
  11. const userModel = require('../models/user.js');
  12. const logModel = require('../models/log.js');
  13. const followModel = require('../models/follow.js');
  14. const tokenModel = require('../models/token.js');
  15. const {getToken} = require('../utils/token')
  16. const sha = require('sha.js');
  17. const axios = require('axios').default;
  18. class projectController extends baseController {
  19. constructor(ctx) {
  20. super(ctx);
  21. this.Model = yapi.getInst(projectModel);
  22. this.groupModel = yapi.getInst(groupModel);
  23. this.logModel = yapi.getInst(logModel);
  24. this.followModel = yapi.getInst(followModel);
  25. this.tokenModel = yapi.getInst(tokenModel);
  26. this.interfaceModel = yapi.getInst(interfaceModel);
  27. const id = 'number';
  28. const member_uid = ['number'];
  29. const name = {
  30. type: 'string',
  31. minLength: 1
  32. };
  33. const role = {
  34. type: 'string',
  35. enum: ['owner', 'dev', 'guest']
  36. };
  37. const basepath = {
  38. type: 'string',
  39. default: ''
  40. };
  41. const group_id = 'number';
  42. const group_name = 'string';
  43. const project_type = {
  44. type: 'string',
  45. enum: ['private', 'public'],
  46. default: 'private'
  47. };
  48. const desc = 'string';
  49. const icon = 'string';
  50. const color = 'string';
  51. const env = 'array';
  52. const cat = 'array';
  53. this.schemaMap = {
  54. add: {
  55. '*name': name,
  56. basepath: basepath,
  57. '*group_id': group_id,
  58. group_name,
  59. desc: desc,
  60. color,
  61. icon,
  62. project_type
  63. },
  64. copy: {
  65. '*name': name,
  66. preName: name,
  67. basepath: basepath,
  68. '*group_id': group_id,
  69. _id: id,
  70. cat,
  71. pre_script: desc,
  72. after_script: desc,
  73. env,
  74. group_name,
  75. desc,
  76. color,
  77. icon,
  78. project_type
  79. },
  80. addMember: {
  81. '*id': id,
  82. '*member_uids': member_uid,
  83. role: role
  84. },
  85. delMember: {
  86. '*id': id,
  87. '*member_uid': id
  88. },
  89. getMemberList: {
  90. '*id': id
  91. },
  92. get: {
  93. 'id': id,
  94. 'project_id': id
  95. },
  96. list: {
  97. '*group_id': group_id
  98. },
  99. del: {
  100. '*id': id
  101. },
  102. changeMemberRole: {
  103. '*id': id,
  104. '*member_uid': id,
  105. role
  106. },
  107. token: {
  108. '*project_id': id
  109. },
  110. updateToken: {
  111. '*project_id': id
  112. }
  113. };
  114. }
  115. handleBasepath(basepath) {
  116. if (!basepath) {
  117. return '';
  118. }
  119. if (basepath === '/') {
  120. return '';
  121. }
  122. if (basepath[0] !== '/') {
  123. basepath = '/' + basepath;
  124. }
  125. if (basepath[basepath.length - 1] === '/') {
  126. basepath = basepath.substr(0, basepath.length - 1);
  127. }
  128. if (!/^\/[a-zA-Z0-9\-\/\._]+$/.test(basepath)) {
  129. return false;
  130. }
  131. return basepath;
  132. }
  133. verifyDomain(domain) {
  134. if (!domain) {
  135. return false;
  136. }
  137. if (/^[a-zA-Z0-9\-_\.]+?\.[a-zA-Z0-9\-_\.]*?[a-zA-Z]{2,6}$/.test(domain)) {
  138. return true;
  139. }
  140. return false;
  141. }
  142. /**
  143. * 判断分组名称是否重复
  144. * @interface /project/check_project_name
  145. * @method get
  146. */
  147. async checkProjectName(ctx) {
  148. try {
  149. let name = ctx.request.query.name;
  150. let group_id = ctx.request.query.group_id;
  151. if (!name) {
  152. return (ctx.body = yapi.commons.resReturn(null, 401, '项目名不能为空'));
  153. }
  154. let checkRepeat = await this.Model.checkNameRepeat(name, group_id);
  155. if (checkRepeat > 0) {
  156. return (ctx.body = yapi.commons.resReturn(null, 401, '已存在的项目名'));
  157. }
  158. ctx.body = yapi.commons.resReturn({});
  159. } catch (err) {
  160. ctx.body = yapi.commons.resReturn(null, 402, err.message);
  161. }
  162. }
  163. /**
  164. * 添加项目分组
  165. * @interface /project/add
  166. * @method POST
  167. * @category project
  168. * @foldnumber 10
  169. * @param {String} name 项目名称,不能为空
  170. * @param {String} basepath 项目基本路径,不能为空
  171. * @param {Number} group_id 项目分组id,不能为空
  172. * @param {Number} group_name 项目分组名称,不能为空
  173. * @param {String} project_type private public
  174. * @param {String} [desc] 项目描述
  175. * @returns {Object}
  176. * @example ./api/project/add.json
  177. */
  178. async add(ctx) {
  179. let params = ctx.params;
  180. if ((await this.checkAuth(params.group_id, 'group', 'edit')) !== true) {
  181. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  182. }
  183. let checkRepeat = await this.Model.checkNameRepeat(params.name, params.group_id);
  184. if (checkRepeat > 0) {
  185. return (ctx.body = yapi.commons.resReturn(null, 401, '已存在的项目名'));
  186. }
  187. params.basepath = params.basepath || '';
  188. if ((params.basepath = this.handleBasepath(params.basepath)) === false) {
  189. return (ctx.body = yapi.commons.resReturn(null, 401, 'basepath格式有误'));
  190. }
  191. let data = {
  192. name: params.name,
  193. desc: params.desc,
  194. basepath: params.basepath,
  195. members: [],
  196. project_type: params.project_type || 'private',
  197. uid: this.getUid(),
  198. group_id: params.group_id,
  199. group_name: params.group_name,
  200. icon: params.icon,
  201. color: params.color,
  202. add_time: yapi.commons.time(),
  203. up_time: yapi.commons.time(),
  204. is_json5: false,
  205. env: [{ name: 'local', domain: 'http://127.0.0.1' }]
  206. };
  207. let result = await this.Model.save(data);
  208. let colInst = yapi.getInst(interfaceColModel);
  209. let catInst = yapi.getInst(interfaceCatModel);
  210. if (result._id) {
  211. await colInst.save({
  212. name: '公共测试集',
  213. project_id: result._id,
  214. desc: '公共测试集',
  215. uid: this.getUid(),
  216. add_time: yapi.commons.time(),
  217. up_time: yapi.commons.time()
  218. });
  219. await catInst.save({
  220. name: '公共分类',
  221. project_id: result._id,
  222. desc: '公共分类',
  223. uid: this.getUid(),
  224. add_time: yapi.commons.time(),
  225. up_time: yapi.commons.time()
  226. });
  227. }
  228. let uid = this.getUid();
  229. // 将项目添加者变成项目组长,除admin以外
  230. if (this.getRole() !== 'admin') {
  231. let userdata = await yapi.commons.getUserdata(uid, 'owner');
  232. await this.Model.addMember(result._id, [userdata]);
  233. }
  234. let username = this.getUsername();
  235. yapi.commons.saveLog({
  236. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 添加了项目 <a href="/project/${
  237. result._id
  238. }">${params.name}</a>`,
  239. type: 'project',
  240. uid,
  241. username: username,
  242. typeid: result._id
  243. });
  244. yapi.emitHook('project_add', result).then();
  245. ctx.body = yapi.commons.resReturn(result);
  246. }
  247. /**
  248. * 拷贝项目分组
  249. * @interface /project/copy
  250. * @method POST
  251. * @category project
  252. * @foldnumber 10
  253. * @param {String} name 项目名称,不能为空
  254. * @param {String} basepath 项目基本路径,不能为空
  255. * @param {Number} group_id 项目分组id,不能为空
  256. * @param {Number} group_name 项目分组名称,不能为空
  257. * @param {String} project_type private public
  258. * @param {String} [desc] 项目描述
  259. * @returns {Object}
  260. * @example ./api/project/add.json
  261. */
  262. async copy(ctx) {
  263. try {
  264. let params = ctx.params;
  265. // 拷贝项目的ID
  266. let copyId = params._id;
  267. if ((await this.checkAuth(params.group_id, 'group', 'edit')) !== true) {
  268. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  269. }
  270. params.basepath = params.basepath || '';
  271. let data = Object.assign(params, {
  272. project_type: params.project_type || 'private',
  273. uid: this.getUid(),
  274. add_time: yapi.commons.time(),
  275. up_time: yapi.commons.time(),
  276. env: params.env || [{ name: 'local', domain: 'http://127.0.0.1' }]
  277. });
  278. delete data._id;
  279. let result = await this.Model.save(data);
  280. let colInst = yapi.getInst(interfaceColModel);
  281. let catInst = yapi.getInst(interfaceCatModel);
  282. // 增加集合
  283. if (result._id) {
  284. await colInst.save({
  285. name: '公共测试集',
  286. project_id: result._id,
  287. desc: '公共测试集',
  288. uid: this.getUid(),
  289. add_time: yapi.commons.time(),
  290. up_time: yapi.commons.time()
  291. });
  292. // 拷贝接口列表
  293. let cat = params.cat;
  294. for (let i = 0; i < cat.length; i++) {
  295. let item = cat[i];
  296. let catDate = {
  297. name: item.name,
  298. project_id: result._id,
  299. desc: item.desc,
  300. uid: this.getUid(),
  301. add_time: yapi.commons.time(),
  302. up_time: yapi.commons.time()
  303. };
  304. let catResult = await catInst.save(catDate);
  305. // 获取每个集合中的interface
  306. let interfaceData = await this.interfaceModel.listByInterStatus(item._id);
  307. // 将interfaceData存到新的catID中
  308. for (let key = 0; key < interfaceData.length; key++) {
  309. let interfaceItem = interfaceData[key].toObject();
  310. let data = Object.assign(interfaceItem, {
  311. uid: this.getUid(),
  312. catid: catResult._id,
  313. project_id: result._id,
  314. add_time: yapi.commons.time(),
  315. up_time: yapi.commons.time()
  316. });
  317. delete data._id;
  318. await this.interfaceModel.save(data);
  319. }
  320. }
  321. }
  322. // 增加member
  323. let copyProject = await this.Model.get(copyId);
  324. let copyProjectMembers = copyProject.members;
  325. let uid = this.getUid();
  326. // 将项目添加者变成项目组长,除admin以外
  327. if (this.getRole() !== 'admin') {
  328. let userdata = await yapi.commons.getUserdata(uid, 'owner');
  329. let check = await this.Model.checkMemberRepeat(copyId, uid);
  330. if (check === 0) {
  331. copyProjectMembers.push(userdata);
  332. }
  333. }
  334. await this.Model.addMember(result._id, copyProjectMembers);
  335. // 在每个测试结合下添加interface
  336. let username = this.getUsername();
  337. yapi.commons.saveLog({
  338. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 复制了项目 ${
  339. params.preName
  340. } 为 <a href="/project/${result._id}">${params.name}</a>`,
  341. type: 'project',
  342. uid,
  343. username: username,
  344. typeid: result._id
  345. });
  346. ctx.body = yapi.commons.resReturn(result);
  347. } catch (err) {
  348. ctx.body = yapi.commons.resReturn(null, 402, err.message);
  349. }
  350. }
  351. /**
  352. * 添加项目成员
  353. * @interface /project/add_member
  354. * @method POST
  355. * @category project
  356. * @foldnumber 10
  357. * @param {Number} id 项目id,不能为空
  358. * @param {Array} member_uid 项目成员uid,不能为空
  359. * @returns {Object}
  360. * @example ./api/project/add_member.json
  361. */
  362. async addMember(ctx) {
  363. let params = ctx.params;
  364. if ((await this.checkAuth(params.id, 'project', 'edit')) !== true) {
  365. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  366. }
  367. params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
  368. let add_members = [];
  369. let exist_members = [];
  370. let no_members = [];
  371. for (let i = 0, len = params.member_uids.length; i < len; i++) {
  372. let id = params.member_uids[i];
  373. let check = await this.Model.checkMemberRepeat(params.id, id);
  374. let userdata = await yapi.commons.getUserdata(id, params.role);
  375. if (check > 0) {
  376. exist_members.push(userdata);
  377. } else if (!userdata) {
  378. no_members.push(id);
  379. } else {
  380. add_members.push(userdata);
  381. }
  382. }
  383. let result = await this.Model.addMember(params.id, add_members);
  384. if (add_members.length) {
  385. let members = add_members.map(item => {
  386. return `<a href = "/user/profile/${item.uid}">${item.username}</a>`;
  387. });
  388. members = members.join('、');
  389. let username = this.getUsername();
  390. yapi.commons.saveLog({
  391. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 添加了项目成员 ${members}`,
  392. type: 'project',
  393. uid: this.getUid(),
  394. username: username,
  395. typeid: params.id
  396. });
  397. }
  398. ctx.body = yapi.commons.resReturn({
  399. result,
  400. add_members,
  401. exist_members,
  402. no_members
  403. });
  404. }
  405. /**
  406. * 删除项目成员
  407. * @interface /project/del_member
  408. * @method POST
  409. * @category project
  410. * @foldnumber 10
  411. * @param {Number} id 项目id,不能为空
  412. * @param {member_uid} uid 项目成员uid,不能为空
  413. * @returns {Object}
  414. * @example ./api/project/del_member.json
  415. */
  416. async delMember(ctx) {
  417. try {
  418. let params = ctx.params;
  419. var check = await this.Model.checkMemberRepeat(params.id, params.member_uid);
  420. if (check === 0) {
  421. return (ctx.body = yapi.commons.resReturn(null, 400, '项目成员不存在'));
  422. }
  423. if ((await this.checkAuth(params.id, 'project', 'danger')) !== true) {
  424. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  425. }
  426. let result = await this.Model.delMember(params.id, params.member_uid);
  427. let username = this.getUsername();
  428. yapi
  429. .getInst(userModel)
  430. .findById(params.member_uid)
  431. .then(member => {
  432. yapi.commons.saveLog({
  433. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 删除了项目中的成员 <a href="/user/profile/${
  434. params.member_uid
  435. }">${member ? member.username : ''}</a>`,
  436. type: 'project',
  437. uid: this.getUid(),
  438. username: username,
  439. typeid: params.id
  440. });
  441. });
  442. ctx.body = yapi.commons.resReturn(result);
  443. } catch (e) {
  444. ctx.body = yapi.commons.resReturn(null, 402, e.message);
  445. }
  446. }
  447. /**
  448. * 获取项目成员列表
  449. * @interface /project/get_member_list
  450. * @method GET
  451. * @category project
  452. * @foldnumber 10
  453. * @param {Number} id 项目id,不能为空
  454. * @return {Object}
  455. * @example ./api/project/get_member_list.json
  456. */
  457. async getMemberList(ctx) {
  458. let params = ctx.params;
  459. if (!params.id) {
  460. return (ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空'));
  461. }
  462. let project = await this.Model.get(params.id);
  463. ctx.body = yapi.commons.resReturn(project.members);
  464. }
  465. /**
  466. * 获取项目信息
  467. * @interface /project/get
  468. * @method GET
  469. * @category project
  470. * @foldnumber 10
  471. * @param {Number} id 项目id,不能为空
  472. * @returns {Object}
  473. * @example ./api/project/get.json
  474. */
  475. async get(ctx) {
  476. let params = ctx.params;
  477. let projectId= params.id || params.project_id; // 通过 token 访问
  478. let result = await this.Model.getBaseInfo(projectId);
  479. if (!result) {
  480. return (ctx.body = yapi.commons.resReturn(null, 400, '不存在的项目'));
  481. }
  482. if (result.project_type === 'private') {
  483. if ((await this.checkAuth(result._id, 'project', 'view')) !== true) {
  484. return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限'));
  485. }
  486. }
  487. result = result.toObject();
  488. let catInst = yapi.getInst(interfaceCatModel);
  489. let cat = await catInst.list(params.id);
  490. result.cat = cat;
  491. if (result.env.length === 0) {
  492. result.env.push({ name: 'local', domain: 'http://127.0.0.1' });
  493. }
  494. result.role = await this.getProjectRole(params.id, 'project');
  495. yapi.emitHook('project_get', result).then();
  496. ctx.body = yapi.commons.resReturn(result);
  497. }
  498. /**
  499. * 获取项目列表
  500. * @interface /project/list
  501. * @method GET
  502. * @category project
  503. * @foldnumber 10
  504. * @param {Number} group_id 项目group_id,不能为空
  505. * @returns {Object}
  506. * @example ./api/project/list.json
  507. */
  508. async list(ctx) {
  509. let group_id = ctx.params.group_id,
  510. project_list = [];
  511. let groupData = await this.groupModel.get(group_id);
  512. let isPrivateGroup = false;
  513. if (groupData.type === 'private' && this.getUid() === groupData.uid) {
  514. isPrivateGroup = true;
  515. }
  516. let auth = await this.checkAuth(group_id, 'group', 'view');
  517. let result = await this.Model.list(group_id);
  518. let follow = await this.followModel.list(this.getUid());
  519. if (isPrivateGroup === false) {
  520. for (let index = 0, item, r = 1; index < result.length; index++) {
  521. item = result[index].toObject();
  522. if (item.project_type === 'private' && auth === false) {
  523. r = await this.Model.checkMemberRepeat(item._id, this.getUid());
  524. if (r === 0) {
  525. continue;
  526. }
  527. }
  528. let f = _.find(follow, fol => {
  529. return fol.projectid === item._id;
  530. });
  531. // 排序:收藏的项目放前面
  532. if (f) {
  533. item.follow = true;
  534. project_list.unshift(item);
  535. } else {
  536. item.follow = false;
  537. project_list.push(item);
  538. }
  539. }
  540. } else {
  541. follow = follow.map(item => {
  542. item = item.toObject();
  543. item.follow = true;
  544. return item;
  545. });
  546. project_list = _.uniq(follow.concat(result), item => item._id);
  547. }
  548. ctx.body = yapi.commons.resReturn({
  549. list: project_list
  550. });
  551. }
  552. /**
  553. * 删除项目
  554. * @interface /project/del
  555. * @method POST
  556. * @category project
  557. * @foldnumber 10
  558. * @param {Number} id 项目id,不能为空
  559. * @returns {Object}
  560. * @example ./api/project/del.json
  561. */
  562. async del(ctx) {
  563. let id = ctx.params.id;
  564. if ((await this.checkAuth(id, 'project', 'danger')) !== true) {
  565. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  566. }
  567. let interfaceInst = yapi.getInst(interfaceModel);
  568. let interfaceColInst = yapi.getInst(interfaceColModel);
  569. let interfaceCaseInst = yapi.getInst(interfaceCaseModel);
  570. await interfaceInst.delByProjectId(id);
  571. await interfaceCaseInst.delByProjectId(id);
  572. await interfaceColInst.delByProjectId(id);
  573. yapi.emitHook('project_del', id).then();
  574. let result = await this.Model.del(id);
  575. ctx.body = yapi.commons.resReturn(result);
  576. }
  577. /**
  578. * 修改项目成员角色
  579. * @interface /project/change_member_role
  580. * @method POST
  581. * @category project
  582. * @foldnumber 10
  583. * @param {String} id 项目id
  584. * @param {String} member_uid 项目成员uid
  585. * @param {String} role 权限 ['owner'|'dev']
  586. * @returns {Object}
  587. * @example
  588. */
  589. async changeMemberRole(ctx) {
  590. let params = ctx.request.body;
  591. let projectInst = yapi.getInst(projectModel);
  592. var check = await projectInst.checkMemberRepeat(params.id, params.member_uid);
  593. if (check === 0) {
  594. return (ctx.body = yapi.commons.resReturn(null, 400, '项目成员不存在'));
  595. }
  596. if ((await this.checkAuth(params.id, 'project', 'danger')) !== true) {
  597. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  598. }
  599. params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
  600. let rolename = {
  601. owner: '组长',
  602. dev: '开发者',
  603. guest: '访客'
  604. };
  605. let result = await projectInst.changeMemberRole(params.id, params.member_uid, params.role);
  606. let username = this.getUsername();
  607. yapi
  608. .getInst(userModel)
  609. .findById(params.member_uid)
  610. .then(member => {
  611. yapi.commons.saveLog({
  612. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 修改了项目中的成员 <a href="/user/profile/${
  613. params.member_uid
  614. }">${member.username}</a> 的角色为 "${rolename[params.role]}"`,
  615. type: 'project',
  616. uid: this.getUid(),
  617. username: username,
  618. typeid: params.id
  619. });
  620. });
  621. ctx.body = yapi.commons.resReturn(result);
  622. }
  623. /**
  624. * 修改项目成员是否收到邮件通知
  625. * @interface /project/change_member_email_notice
  626. * @method POST
  627. * @category project
  628. * @foldnumber 10
  629. * @param {String} id 项目id
  630. * @param {String} member_uid 项目成员uid
  631. * @param {String} role 权限 ['owner'|'dev']
  632. * @returns {Object}
  633. * @example
  634. */
  635. async changeMemberEmailNotice(ctx) {
  636. try {
  637. let params = ctx.request.body;
  638. let projectInst = yapi.getInst(projectModel);
  639. var check = await projectInst.checkMemberRepeat(params.id, params.member_uid);
  640. if (check === 0) {
  641. return (ctx.body = yapi.commons.resReturn(null, 400, '项目成员不存在'));
  642. }
  643. let result = await projectInst.changeMemberEmailNotice(
  644. params.id,
  645. params.member_uid,
  646. params.notice
  647. );
  648. ctx.body = yapi.commons.resReturn(result);
  649. } catch (e) {
  650. ctx.body = yapi.commons.resReturn(null, 402, e.message);
  651. }
  652. }
  653. /**
  654. * 项目头像设置
  655. * @interface /project/upset
  656. * @method POST
  657. * @category project
  658. * @foldnumber 10
  659. * @param {Number} id 项目id,不能为空
  660. * @param {String} icon 项目icon
  661. * @param {Array} color 项目color
  662. * @returns {Object}
  663. * @example ./api/project/upset
  664. */
  665. async upSet(ctx) {
  666. let id = ctx.request.body.id;
  667. let data = {};
  668. if ((await this.checkAuth(id, 'project', 'danger')) !== true) {
  669. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  670. }
  671. data.color = ctx.request.body.color;
  672. data.icon = ctx.request.body.icon;
  673. if (!id) {
  674. return (ctx.body = yapi.commons.resReturn(null, 405, '项目id不能为空'));
  675. }
  676. try {
  677. let result = await this.Model.up(id, data);
  678. ctx.body = yapi.commons.resReturn(result);
  679. } catch (e) {
  680. ctx.body = yapi.commons.resReturn(null, 402, e.message);
  681. }
  682. try {
  683. this.followModel.updateById(this.getUid(), id, data).then(() => {
  684. let username = this.getUsername();
  685. yapi.commons.saveLog({
  686. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 修改了项目图标、颜色`,
  687. type: 'project',
  688. uid: this.getUid(),
  689. username: username,
  690. typeid: id
  691. });
  692. });
  693. } catch (e) {
  694. yapi.commons.log(e, 'error'); // eslint-disable-line
  695. }
  696. }
  697. /**
  698. * 编辑项目
  699. * @interface /project/up
  700. * @method POST
  701. * @category project
  702. * @foldnumber 10
  703. * @param {Number} id 项目id,不能为空
  704. * @param {String} name 项目名称,不能为空
  705. * @param {String} basepath 项目基本路径,不能为空
  706. * @param {String} [desc] 项目描述
  707. * @returns {Object}
  708. * @example ./api/project/up.json
  709. */
  710. async up(ctx) {
  711. try {
  712. let id = ctx.request.body.id;
  713. let params = ctx.request.body;
  714. params = yapi.commons.handleParams(params, {
  715. name: 'string',
  716. basepath: 'string',
  717. group_id: 'number',
  718. desc: 'string',
  719. pre_script: 'string',
  720. after_script: 'string',
  721. project_mock_script: 'string'
  722. });
  723. if (!id) {
  724. return (ctx.body = yapi.commons.resReturn(null, 405, '项目id不能为空'));
  725. }
  726. if ((await this.checkAuth(id, 'project', 'danger')) !== true) {
  727. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  728. }
  729. let projectData = await this.Model.get(id);
  730. if (params.basepath) {
  731. if ((params.basepath = this.handleBasepath(params.basepath)) === false) {
  732. return (ctx.body = yapi.commons.resReturn(null, 401, 'basepath格式有误'));
  733. }
  734. }
  735. if (projectData.name === params.name) {
  736. delete params.name;
  737. }
  738. if (params.name) {
  739. let checkRepeat = await this.Model.checkNameRepeat(params.name, params.group_id);
  740. if (checkRepeat > 0) {
  741. return (ctx.body = yapi.commons.resReturn(null, 401, '已存在的项目名'));
  742. }
  743. }
  744. let data = {
  745. up_time: yapi.commons.time()
  746. };
  747. data = Object.assign({}, data, params);
  748. let result = await this.Model.up(id, data);
  749. let username = this.getUsername();
  750. yapi.commons.saveLog({
  751. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 更新了项目 <a href="/project/${id}/interface/api">${
  752. projectData.name
  753. }</a>`,
  754. type: 'project',
  755. uid: this.getUid(),
  756. username: username,
  757. typeid: id
  758. });
  759. yapi.emitHook('project_up', result).then();
  760. ctx.body = yapi.commons.resReturn(result);
  761. } catch (e) {
  762. ctx.body = yapi.commons.resReturn(null, 402, e.message);
  763. }
  764. }
  765. /**
  766. * 编辑项目
  767. * @interface /project/up_env
  768. * @method POST
  769. * @category project
  770. * @foldnumber 10
  771. * @param {Number} id 项目id,不能为空
  772. * @param {Array} [env] 项目环境配置
  773. * @param {String} [env[].name] 环境名称
  774. * @param {String} [env[].domain] 环境域名
  775. * @param {Array} [env[].header] header
  776. * @returns {Object}
  777. * @example
  778. */
  779. async upEnv(ctx) {
  780. try {
  781. let id = ctx.request.body.id;
  782. let params = ctx.request.body;
  783. if (!id) {
  784. return (ctx.body = yapi.commons.resReturn(null, 405, '项目id不能为空'));
  785. }
  786. if ((await this.checkAuth(id, 'project', 'edit')) !== true) {
  787. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  788. }
  789. if (!params.env || !Array.isArray(params.env)) {
  790. return (ctx.body = yapi.commons.resReturn(null, 405, 'env参数格式有误'));
  791. }
  792. let projectData = await this.Model.get(id);
  793. let data = {
  794. up_time: yapi.commons.time()
  795. };
  796. data.env = params.env;
  797. let isRepeat = this.arrRepeat(data.env, 'name');
  798. if (isRepeat) {
  799. return (ctx.body = yapi.commons.resReturn(null, 405, '环境变量名重复'));
  800. }
  801. let result = await this.Model.up(id, data);
  802. let username = this.getUsername();
  803. yapi.commons.saveLog({
  804. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 更新了项目 <a href="/project/${id}/interface/api">${
  805. projectData.name
  806. }</a> 的环境`,
  807. type: 'project',
  808. uid: this.getUid(),
  809. username: username,
  810. typeid: id
  811. });
  812. ctx.body = yapi.commons.resReturn(result);
  813. } catch (e) {
  814. ctx.body = yapi.commons.resReturn(null, 402, e.message);
  815. }
  816. }
  817. /**
  818. * 编辑项目
  819. * @interface /project/up_tag
  820. * @method POST
  821. * @category project
  822. * @foldnumber 10
  823. * @param {Number} id 项目id,不能为空
  824. * @param {Array} [tag] 项目tag配置
  825. * @param {String} [tag[].name] tag名称
  826. * @param {String} [tag[].desc] tag描述
  827. * @returns {Object}
  828. * @example
  829. */
  830. async upTag(ctx) {
  831. try {
  832. let id = ctx.request.body.id;
  833. let params = ctx.request.body;
  834. if (!id) {
  835. return (ctx.body = yapi.commons.resReturn(null, 405, '项目id不能为空'));
  836. }
  837. if ((await this.checkAuth(id, 'project', 'edit')) !== true) {
  838. return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  839. }
  840. if (!params.tag || !Array.isArray(params.tag)) {
  841. return (ctx.body = yapi.commons.resReturn(null, 405, 'tag参数格式有误'));
  842. }
  843. let projectData = await this.Model.get(id);
  844. let data = {
  845. up_time: yapi.commons.time()
  846. };
  847. data.tag = params.tag;
  848. let result = await this.Model.up(id, data);
  849. let username = this.getUsername();
  850. yapi.commons.saveLog({
  851. content: `<a href="/user/profile/${this.getUid()}">${username}</a> 更新了项目 <a href="/project/${id}/interface/api">${
  852. projectData.name
  853. }</a> 的tag`,
  854. type: 'project',
  855. uid: this.getUid(),
  856. username: username,
  857. typeid: id
  858. });
  859. ctx.body = yapi.commons.resReturn(result);
  860. } catch (e) {
  861. ctx.body = yapi.commons.resReturn(null, 402, e.message);
  862. }
  863. }
  864. /**
  865. * 获取项目的环境变量值
  866. * @interface /project/get_env
  867. * @method GET
  868. * @category project
  869. * @foldnumber 10
  870. * @param {Number} id 项目id,不能为空
  871. * @returns {Object}
  872. * @example
  873. */
  874. async getEnv(ctx) {
  875. try {
  876. // console.log(ctx.request.query.project_id)
  877. let project_id = ctx.request.query.project_id;
  878. // let params = ctx.request.body;
  879. if (!project_id) {
  880. return (ctx.body = yapi.commons.resReturn(null, 405, '项目id不能为空'));
  881. }
  882. // 去掉权限判断
  883. // if ((await this.checkAuth(project_id, 'project', 'edit')) !== true) {
  884. // return (ctx.body = yapi.commons.resReturn(null, 405, '没有权限'));
  885. // }
  886. let env = await this.Model.getByEnv(project_id);
  887. ctx.body = yapi.commons.resReturn(env);
  888. } catch (e) {
  889. ctx.body = yapi.commons.resReturn(null, 402, e.message);
  890. }
  891. }
  892. arrRepeat(arr, key) {
  893. const s = new Set();
  894. arr.forEach(item => s.add(item[key]));
  895. return s.size !== arr.length;
  896. }
  897. /**
  898. * 获取token数据
  899. * @interface /project/token
  900. * @method GET
  901. * @category project
  902. * @foldnumber 10
  903. * @param {Number} id 项目id,不能为空
  904. * @param {String} q
  905. * @return {Object}
  906. */
  907. async token(ctx) {
  908. try {
  909. let project_id = ctx.params.project_id;
  910. let data = await this.tokenModel.get(project_id);
  911. let token;
  912. if (!data) {
  913. let passsalt = yapi.commons.randStr();
  914. token = sha('sha1')
  915. .update(passsalt)
  916. .digest('hex')
  917. .substr(0, 20);
  918. await this.tokenModel.save({ project_id, token });
  919. } else {
  920. token = data.token;
  921. }
  922. token = getToken(token, this.getUid())
  923. ctx.body = yapi.commons.resReturn(token);
  924. } catch (err) {
  925. ctx.body = yapi.commons.resReturn(null, 402, err.message);
  926. }
  927. }
  928. /**
  929. * 更新token数据
  930. * @interface /project/update_token
  931. * @method GET
  932. * @category project
  933. * @foldnumber 10
  934. * @param {Number} id 项目id,不能为空
  935. * @param {String} q
  936. * @return {Object}
  937. */
  938. async updateToken(ctx) {
  939. try {
  940. let project_id = ctx.params.project_id;
  941. let data = await this.tokenModel.get(project_id);
  942. let token, result;
  943. if (data && data.token) {
  944. let passsalt = yapi.commons.randStr();
  945. token = sha('sha1')
  946. .update(passsalt)
  947. .digest('hex')
  948. .substr(0, 20);
  949. result = await this.tokenModel.up(project_id, token);
  950. token = getToken(token);
  951. result.token = token;
  952. } else {
  953. ctx.body = yapi.commons.resReturn(null, 402, '没有查到token信息');
  954. }
  955. ctx.body = yapi.commons.resReturn(result);
  956. } catch (err) {
  957. ctx.body = yapi.commons.resReturn(null, 402, err.message);
  958. }
  959. }
  960. /**
  961. * 模糊搜索项目名称或者分组名称或接口名称
  962. * @interface /project/search
  963. * @method GET
  964. * @category project
  965. * @foldnumber 10
  966. * @param {String} q
  967. * @return {Object}
  968. * @example ./api/project/search.json
  969. */
  970. async search(ctx) {
  971. const { q } = ctx.request.query;
  972. if (!q) {
  973. return (ctx.body = yapi.commons.resReturn(void 0, 400, 'No keyword.'));
  974. }
  975. if (!yapi.commons.validateSearchKeyword(q)) {
  976. return (ctx.body = yapi.commons.resReturn(void 0, 400, 'Bad query.'));
  977. }
  978. let projectList = await this.Model.search(q);
  979. let groupList = await this.groupModel.search(q);
  980. let interfaceList = await this.interfaceModel.search(q);
  981. let projectRules = [
  982. '_id',
  983. 'name',
  984. 'basepath',
  985. 'uid',
  986. 'env',
  987. 'members',
  988. { key: 'group_id', alias: 'groupId' },
  989. { key: 'up_time', alias: 'upTime' },
  990. { key: 'add_time', alias: 'addTime' }
  991. ];
  992. let groupRules = [
  993. '_id',
  994. 'uid',
  995. { key: 'group_name', alias: 'groupName' },
  996. { key: 'group_desc', alias: 'groupDesc' },
  997. { key: 'add_time', alias: 'addTime' },
  998. { key: 'up_time', alias: 'upTime' }
  999. ];
  1000. let interfaceRules = [
  1001. '_id',
  1002. 'uid',
  1003. { key: 'title', alias: 'title' },
  1004. { key: 'project_id', alias: 'projectId' },
  1005. { key: 'add_time', alias: 'addTime' },
  1006. { key: 'up_time', alias: 'upTime' }
  1007. ];
  1008. projectList = commons.filterRes(projectList, projectRules);
  1009. groupList = commons.filterRes(groupList, groupRules);
  1010. interfaceList = commons.filterRes(interfaceList, interfaceRules);
  1011. let queryList = {
  1012. project: projectList,
  1013. group: groupList,
  1014. interface: interfaceList
  1015. };
  1016. return (ctx.body = yapi.commons.resReturn(queryList, 0, 'ok'));
  1017. }
  1018. // 输入 swagger url 的时候 node 端请求数据
  1019. async swaggerUrl(ctx) {
  1020. try {
  1021. const { url } = ctx.request.query;
  1022. const { data } = await axios.get(url);
  1023. if (data == null || typeof data !== 'object') {
  1024. throw new Error('返回数据格式不是 JSON');
  1025. }
  1026. ctx.body = yapi.commons.resReturn(data);
  1027. } catch (err) {
  1028. ctx.body = yapi.commons.resReturn(null, 402, String(err));
  1029. }
  1030. }
  1031. }
  1032. module.exports = projectController;