repoController.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  1. /**
  2. * icon repo Controller
  3. *
  4. */
  5. let config = require("../config/config");
  6. let constant = require("../config/constant");
  7. let responseFormat = require("../util/responseFormat");
  8. let repoInfoRules = require("../validation/repoInfoRules");
  9. let validator = require("../util/validator");
  10. let db = require("../database");
  11. let log = require("../util/log");
  12. let path = require("path");
  13. let fileUtil = require("../util/fileUtil");
  14. let incUtil = require("../util/incUtil");
  15. let icon = require("../tool/icon");
  16. let svgSprite = require("../tool/svgSprite");
  17. let uploadService = require("../service/upload");
  18. class RepoController {
  19. /**
  20. * add icon repo
  21. *
  22. * @param {Object} ctx request object
  23. * @return {void}
  24. */
  25. async addRepo(ctx) {
  26. let params = ctx.request.body || {};
  27. let userInfo = ctx.userInfo;
  28. // validate completely
  29. validator.validateParamsField(params, repoInfoRules, ctx);
  30. // validate unique
  31. await this.iconRepoExist(params, userInfo);
  32. // get increment repoId
  33. let repoId = await incUtil.getIncId({ model: "iconRepo", field: "repoId" });
  34. // build repo data
  35. Object.assign(params, {
  36. createTime: global.globalConfig.nowTime,
  37. updateTime: global.globalConfig.nowTime,
  38. ownerId: userInfo.userId,
  39. repoId: repoId,
  40. });
  41. log.debug(`add repo data: ${JSON.stringify(params)}`);
  42. // save icon repo data to database
  43. await db.iconRepo.add(params);
  44. // add repoId to owner repos field
  45. await db.user.update(
  46. {
  47. userId: userInfo.userId,
  48. },
  49. {
  50. $push: {
  51. repos: {
  52. repoId: repoId,
  53. repoName: params.repoName,
  54. },
  55. },
  56. }
  57. );
  58. ctx.body = responseFormat.responseFormat(200, "save success!", true);
  59. }
  60. /**
  61. * add icon repo
  62. *
  63. * @param {Object} ctx request object
  64. * @return {void}
  65. */
  66. async updateRepoInfo(ctx) {
  67. let data = ctx.request.body || {};
  68. let params = ctx.params;
  69. let userInfo = ctx.userInfo;
  70. // validate completely
  71. validator.validateParamsField(params, repoInfoRules, ctx);
  72. // validate privilege, only owner
  73. let repoItem = await db.iconRepo.findOne({
  74. ownerId: userInfo.userId,
  75. repoId: params.repoId,
  76. });
  77. if (!repoItem) {
  78. throw new Error("no privilege!");
  79. }
  80. // validate unique
  81. // await this.iconRepoExist(data, userInfo);
  82. // build repo data
  83. data = Object.assign(repoItem, data, {
  84. updateTime: global.globalConfig.nowTime,
  85. ownerId: userInfo.userId,
  86. unSync: true,
  87. repoId: parseInt(params.repoId),
  88. });
  89. log.debug(
  90. `user ${userInfo.userId} update repo data: ${JSON.stringify(params)}`
  91. );
  92. // update icon repo data to database
  93. await db.iconRepo.update({ repoId: params.repoId }, data);
  94. // add repoId to owner repos field, todo
  95. // await db.user.update({
  96. // userId: userInfo.userId
  97. // }, {
  98. // "$set": {
  99. // "repos.$[element]": {
  100. // repoId: params.repoId,
  101. // repoName: data.repoName
  102. // }
  103. // }
  104. // }, {
  105. // arrayFilters: [{
  106. // repoId: params.repoId,
  107. // repoName: repoItem.repoName
  108. // }]
  109. // });
  110. ctx.body = responseFormat.responseFormat(200, "save success!", true);
  111. }
  112. /**
  113. * get repo list
  114. *
  115. * @param {Object} ctx request object
  116. * @return {void}
  117. */
  118. async getRepoList(ctx) {
  119. let params = ctx.params;
  120. let query = ctx.request.query || {};
  121. let repoList = [];
  122. if (parseInt(params.userId)) {
  123. repoList = await this.getRepoListByUserId(params, query, ctx);
  124. } else {
  125. repoList = await this.getAllRepoList(query);
  126. }
  127. ctx.body = responseFormat.responseFormatList(200, "", repoList, query);
  128. }
  129. /**
  130. * get all repo list
  131. *
  132. * @param {Object} query pagination object
  133. * @return {void}
  134. */
  135. async getAllRepoList(query) {
  136. let result = await db.iconRepo.find(
  137. {},
  138. global.globalConfig.iconRepoExportFields,
  139. {
  140. limit: parseInt(query.pageSize),
  141. skip: parseInt((query.pageIndex - 1) * query.pageSize),
  142. }
  143. );
  144. query.totalCount = await db.iconRepo.count({});
  145. log.debug(`get all repos and count: ${query.totalCount}`);
  146. // traverse repo list for finding icon that belong to repo
  147. for (let i = 0; i < (result || []).length; i++) {
  148. result[i].icons = [];
  149. result[i].iconCount = (result[i].iconIds || []).length;
  150. for (
  151. let j = 0;
  152. j <
  153. Math.min(
  154. result[i].iconIds.length,
  155. constant.REPO_LIST_CONTAIN_ICON_COUNT_PER_REPO
  156. );
  157. j++
  158. ) {
  159. let iconItem = await db.icon.findOne(
  160. {
  161. iconId: result[i].iconIds[j].iconId,
  162. },
  163. global.globalConfig.iconExportFields
  164. );
  165. result[i].icons.push(iconItem || {});
  166. }
  167. }
  168. return result;
  169. }
  170. /**
  171. * get repo list of specific user
  172. *
  173. * @param {Object} params query object of url
  174. * @param {Object} query pagination object
  175. * @return {void}
  176. */
  177. async getRepoListByUserId(params, query) {
  178. let userItem = await db.user.findOne({
  179. userId: params.userId,
  180. });
  181. query.totalCount = userItem.repos.length;
  182. log.debug(
  183. `get user ${params.userId} repo list and count: ${query.totalCount}`
  184. );
  185. let repoList = [];
  186. for (
  187. let r = (query.pageIndex - 1) * query.pageSize;
  188. r < Math.min(query.pageIndex * query.pageSize, userItem.repos.length);
  189. r++
  190. ) {
  191. let repoItem = await db.iconRepo.findOne(
  192. {
  193. repoId: userItem.repos[r].repoId,
  194. },
  195. global.globalConfig.iconRepoExportFields
  196. );
  197. // traverse repo list for finding icon that belong to repo
  198. repoItem.icons = [];
  199. repoItem.iconCount = (repoItem.iconIds || []).length;
  200. for (
  201. let j = 0;
  202. j <
  203. Math.min(
  204. repoItem.iconIds.length,
  205. constant.REPO_LIST_CONTAIN_ICON_COUNT_PER_REPO
  206. );
  207. j++
  208. ) {
  209. let iconItem = await db.icon.findOne(
  210. {
  211. iconId: repoItem.iconIds[j].iconId,
  212. },
  213. global.globalConfig.iconExportFields
  214. );
  215. repoItem.icons.push(iconItem || {});
  216. }
  217. repoList.push(repoItem);
  218. }
  219. return repoList;
  220. }
  221. /**
  222. * get repo info
  223. *
  224. * @param {Object} ctx request object
  225. * @return {void}
  226. */
  227. async getRepoInfo(ctx) {
  228. let userInfo = ctx.userInfo;
  229. let params = ctx.params;
  230. // find info first
  231. let result = await db.iconRepo.findOne({
  232. repoId: params.repoId,
  233. });
  234. // pre login
  235. if (userInfo.userId) {
  236. // check user has repoId in repos field
  237. let userItem = await db.user.findOne({
  238. userId: userInfo.userId,
  239. });
  240. // isMember if exist repoId
  241. userItem.repos.forEach((item) => {
  242. if (item.repoId === parseInt(params.repoId)) {
  243. result.isMember = true;
  244. }
  245. });
  246. // is owner if repo's ownerId equal to userId
  247. if (result.ownerId === userInfo.userId) {
  248. result.isOwner = true;
  249. }
  250. }
  251. ctx.body = responseFormat.responseFormat(200, "", result);
  252. }
  253. /**
  254. * get repo css or svg resource
  255. *
  256. * @param {Object} ctx request object
  257. * @return {void}
  258. */
  259. async getRepoResource(ctx) {
  260. let params = ctx.params;
  261. let type = (ctx.request.query || {}).type;
  262. // find info first
  263. let repoItem = await db.iconRepo.findOne({
  264. repoId: params.repoId,
  265. });
  266. // pre login
  267. let result = null;
  268. if (repoItem) {
  269. // type can be cssUrl or cssContent
  270. if (repoItem[type]) {
  271. result = repoItem[type];
  272. } else {
  273. let iconIds = await this.getRepoIconIds(repoItem.repoId);
  274. result = [];
  275. for (let iconId of iconIds) {
  276. let iconItem = await db.icon.findOne({ iconId: iconId });
  277. result.push({
  278. iconId: iconItem.iconId,
  279. iconContent: iconItem.iconContent,
  280. iconName: iconItem.iconName,
  281. });
  282. }
  283. }
  284. }
  285. ctx.body = responseFormat.responseFormat(200, "", result);
  286. }
  287. /**
  288. * get iconIds that belong to repo
  289. *
  290. * @param {Number} repoId repoId
  291. * @return {Array} all iconIds that belong to repo
  292. */
  293. async getRepoIconIds(repoId) {
  294. let iconRepoItem = await db.iconRepo.findOne({
  295. repoId: repoId,
  296. });
  297. let iconIds = [];
  298. iconRepoItem.iconIds.map((icon) => {
  299. iconIds.push(icon.iconId);
  300. });
  301. return iconIds;
  302. }
  303. /**
  304. * add iconId to repo's iconIds field
  305. *
  306. * @param {Object} ctx request object
  307. * @return {void}
  308. */
  309. async addIcon2Repo(ctx) {
  310. let params = ctx.request.body || {};
  311. let userInfo = ctx.userInfo;
  312. // judge unique from upload icons
  313. let tmpMap = {};
  314. for (let icon of params.icons) {
  315. if (tmpMap[icon.iconName]) {
  316. throw new Error(`upload repeat icon ${icon.iconName}`);
  317. }
  318. tmpMap[icon.iconName] = true;
  319. }
  320. // has privilege or not
  321. let userItem = await db.user.findOne({
  322. userId: userInfo.userId,
  323. });
  324. if (!userItem || !this.hasRepo(userItem.repos, params.repoId)) {
  325. throw new Error("no privilege!");
  326. }
  327. let repoItem = await db.iconRepo.findOne({
  328. repoId: params.repoId,
  329. });
  330. // judge unique to avoid repeat between upload and database
  331. for (let icon of params.icons) {
  332. for (let existIcon of repoItem.iconIds) {
  333. if (
  334. icon.iconId === existIcon.iconId ||
  335. icon.iconName === existIcon.iconName
  336. ) {
  337. throw new Error(
  338. `repo ${repoItem.repoName} has contain icon ${icon.iconName}`
  339. );
  340. }
  341. }
  342. }
  343. // add many icon one times, need to traverse
  344. for (let icon of params.icons) {
  345. await db.iconRepo.update(
  346. {
  347. repoId: params.repoId,
  348. },
  349. {
  350. $push: {
  351. iconIds: {
  352. iconId: icon.iconId,
  353. iconName: icon.iconName,
  354. },
  355. },
  356. unSync: true,
  357. }
  358. );
  359. log.debug(
  360. `user ${userInfo.userId} add icon ${icon.iconId}-${icon.iconName} to repo ${params.repoId}`
  361. );
  362. await db.iconBelongToRepo.update(
  363. {
  364. iconId: icon.iconId,
  365. },
  366. {
  367. iconId: icon.iconId,
  368. iconName: icon.iconName,
  369. $push: {
  370. repos: {
  371. repoId: repoItem.repoId,
  372. repoName: repoItem.repoName,
  373. },
  374. },
  375. },
  376. {
  377. upsert: true,
  378. }
  379. );
  380. }
  381. ctx.body = responseFormat.responseFormat(200, "add success!", true);
  382. }
  383. /**
  384. * is member or master of repoId
  385. *
  386. * @param {Array} repos repo array
  387. * @param {Number} repoId repo id
  388. * @return {void}
  389. */
  390. hasRepo(repos = [], repoId) {
  391. for (let repo of repos) {
  392. if (repo.repoId === parseInt(repoId)) {
  393. return true;
  394. }
  395. }
  396. return false;
  397. }
  398. /**
  399. * delete iconId from repo
  400. *
  401. * @param {Object} ctx request object
  402. * @return {void}
  403. */
  404. async deleteIconFromRepo(ctx) {
  405. let params = ctx.params || {};
  406. let userInfo = ctx.userInfo;
  407. let isMember = false;
  408. // check user has repoId in repos field
  409. let userItem = await db.user.findOne({
  410. userId: userInfo.userId,
  411. });
  412. // isMember if exist repoId
  413. userItem.repos.forEach((item) => {
  414. if (item.repoId === params.repoId) {
  415. isMember = true;
  416. }
  417. });
  418. if (!isMember) {
  419. ctx.body = responseFormat.responseFormat(403, "no privilege!", false);
  420. }
  421. await db.iconRepo.update(
  422. {
  423. repoId: params.repoId,
  424. },
  425. {
  426. $pull: {
  427. iconIds: {
  428. iconId: params.iconId,
  429. },
  430. },
  431. unSync: true,
  432. }
  433. );
  434. // remove relationship for delete operation
  435. await db.iconBelongToRepo.update(
  436. {
  437. iconId: params.iconId,
  438. },
  439. {
  440. $pull: {
  441. repos: {
  442. repoId: params.repoId,
  443. },
  444. },
  445. }
  446. );
  447. // remove svg file from resource
  448. let repoItem = await db.iconRepo.findOne({
  449. repoId: params.repoId,
  450. });
  451. let iconItem = await db.icon.findOne({
  452. iconId: params.iconId,
  453. });
  454. let iconPath = path.resolve(
  455. __dirname,
  456. `../../public/resource/repository/${repoItem.ownerId}-${repoItem.iconPrefix}/svg/${iconItem.iconName}.svg`
  457. );
  458. await fileUtil.deleteFile(iconPath);
  459. log.debug(
  460. `user ${userInfo.userId} delete icon ${params.iconId} from repo ${params.repoId}`
  461. );
  462. ctx.body = responseFormat.responseFormat(200, "delete success!", true);
  463. }
  464. /**
  465. * sync database and generate css files
  466. *
  467. * @param {Object} ctx request object
  468. * @return {void}
  469. */
  470. async syncRepo(ctx) {
  471. let params = ctx.params;
  472. let userInfo = ctx.userInfo;
  473. let isRepoMember = false;
  474. // judge privilege
  475. let userItem = await db.user.findOne({
  476. userId: userInfo.userId,
  477. });
  478. userItem.repos.map((item) => {
  479. if (item.userId === params.userId) {
  480. isRepoMember = true;
  481. }
  482. });
  483. if (!isRepoMember) {
  484. ctx.body = responseFormat.responseFormat(403, "no privilege!", false);
  485. return;
  486. }
  487. let repoItem = await db.iconRepo.findOne({
  488. repoId: params.repoId,
  489. });
  490. // judge update or not
  491. if (repoItem.unSync) {
  492. let repoPath = path.join(
  493. config.rootRepoPath,
  494. `./${repoItem.ownerId}-${repoItem.iconPrefix}`
  495. );
  496. // clean all svg for avoid cache or change iconPrefix, maintain if default
  497. // if (config.productType === 'default') {
  498. // await fileUtil.deleteDirector(repoPath);
  499. // }
  500. let repoSvgPath = path.join(repoPath, "./svg/");
  501. let repoIcons = [];
  502. await fileUtil.createDirector(repoSvgPath);
  503. // create svg file recursive
  504. for (let k = 0; k < repoItem.iconIds.length; k++) {
  505. let iconItem =
  506. (await db.icon.findOne({
  507. iconId: repoItem.iconIds[k].iconId,
  508. })) || {};
  509. repoIcons.push(iconItem);
  510. await fileUtil.createFile(
  511. path.join(repoSvgPath, iconItem.iconName + ".svg"),
  512. iconItem.iconContent
  513. );
  514. }
  515. let uploadResult = {};
  516. let svgSpriteResult = "";
  517. // must has svg
  518. if (repoItem.iconIds.length > 0) {
  519. await icon.compileSvg2Icon(
  520. repoPath,
  521. repoItem.iconPrefix,
  522. repoItem.fontPath
  523. );
  524. // output to server and return css url and css content
  525. uploadResult = await uploadService.upload(repoPath);
  526. // svg sprite
  527. svgSpriteResult = svgSprite(repoItem.iconPrefix, repoIcons);
  528. } else {
  529. uploadResult = { url: "", cssContent: "" };
  530. svgSpriteResult = "";
  531. }
  532. // update sync status
  533. await db.iconRepo.update(
  534. {
  535. repoId: params.repoId,
  536. },
  537. {
  538. unSync: false,
  539. cssUrl: uploadResult.url,
  540. cssContent: uploadResult.cssContent,
  541. svgSpriteContent: svgSpriteResult,
  542. }
  543. );
  544. log.debug(`user ${userInfo.userId} sync repo ${params.repoId} success`);
  545. // clean all svg for avoid cache or change iconPrefix, delete if access thirdly upload
  546. // if (config.productType !== 'default') {
  547. // await fileUtil.deleteDirector(repoPath);
  548. // }
  549. ctx.body = responseFormat.responseFormat(
  550. 200,
  551. "update success!",
  552. uploadResult
  553. );
  554. return;
  555. }
  556. ctx.body = responseFormat.responseFormat(200, "update success!", true);
  557. }
  558. /**
  559. * judge repo already exist
  560. *
  561. * @param {Object} params post request object
  562. * @param {Object} userInfo userInfo
  563. * @return {void}
  564. */
  565. async iconRepoExist(params, userInfo) {
  566. let iconLibItem = await db.iconRepo.findOne({
  567. iconPrefix: params.iconPrefix,
  568. ownerId: userInfo.userId,
  569. });
  570. if (iconLibItem) {
  571. throw new Error(`${params.iconPrefix} already exist!`);
  572. }
  573. }
  574. /**
  575. * add member of repo
  576. *
  577. * @param {Object} ctx request object
  578. * @return {void}
  579. */
  580. async addMember(ctx) {
  581. let params = ctx.params;
  582. let userInfo = ctx.userInfo;
  583. let data = ctx.request.body || {};
  584. // only master can add member
  585. let repo = await db.iconRepo.findOne({
  586. ownerId: userInfo.userId,
  587. repoId: params.repoId,
  588. });
  589. if (!repo) {
  590. ctx.body = responseFormat.responseFormat(403, "no privilege!", false);
  591. return;
  592. }
  593. // user exist
  594. let user = null;
  595. if (parseInt(data.accountType) === constant.MEMBER_ACCOUNT_TYPE_USER_ID) {
  596. user = await db.user.findOne({
  597. userId: data.account,
  598. });
  599. } else {
  600. user = await db.user.findOne({
  601. userName: data.account,
  602. });
  603. }
  604. if (!user) {
  605. ctx.body = responseFormat.responseFormat(
  606. 200,
  607. "user not exist, make sure has login the site",
  608. false
  609. );
  610. return;
  611. }
  612. // add member of repo
  613. await db.user.update(
  614. {
  615. userId: user.userId,
  616. },
  617. {
  618. $push: {
  619. repos: {
  620. repoId: repo.repoId,
  621. repoName: repo.repoName,
  622. },
  623. },
  624. }
  625. );
  626. log.debug(
  627. `user ${userInfo.userId} add member ${user.userId} of repo ${repo.repoId}`
  628. );
  629. ctx.body = responseFormat.responseFormat(200, "添加成功!", true);
  630. }
  631. /**
  632. * get recommend repo list
  633. *
  634. * @param {Object} ctx request object
  635. * @return {void}
  636. */
  637. async getRecommendRepoList(ctx) {
  638. let recommendRepos = await db.repoRecommend.find({});
  639. let result = [];
  640. let query = ctx.request.query || {};
  641. // find all recommend repo
  642. for (let repo of recommendRepos) {
  643. let repoItem = await db.iconRepo.findOne(
  644. {
  645. repoId: repo.repoId,
  646. },
  647. global.globalConfig.iconRepoExportFields,
  648. {
  649. lean: true,
  650. }
  651. );
  652. // traverse repo list for finding icon that belong to repo
  653. repoItem.icons = [];
  654. repoItem.iconCount = (repoItem.iconIds || []).length;
  655. for (let icon of repoItem.iconIds) {
  656. if (
  657. repoItem.icons.length >=
  658. constant.REPO_LIST_CONTAIN_ICON_COUNT_PER_REPO
  659. ) {
  660. break;
  661. }
  662. let iconItem =
  663. (await db.icon.findOne(
  664. {
  665. iconId: icon.iconId,
  666. },
  667. global.globalConfig.iconExportFields
  668. )) || {};
  669. repoItem.icons.push(iconItem);
  670. }
  671. result.push(repoItem);
  672. }
  673. ctx.body = responseFormat.responseFormatList(200, "", result, query);
  674. }
  675. /**
  676. * add recommend repo
  677. *
  678. * @param {Object} ctx request object
  679. * @return {void}
  680. */
  681. async addRecommendRepo(ctx) {
  682. let params = ctx.request.body || {};
  683. if (params.key === config.salt) {
  684. for (let item of params.repos) {
  685. let repo = await db.iconRepo.findOne({
  686. repoId: item.repoId,
  687. });
  688. if (repo) {
  689. // get increment repoId
  690. let id = await incUtil.getIncId({
  691. model: "repoRecommend",
  692. field: "id",
  693. });
  694. await db.repoRecommend.add({
  695. id: id,
  696. repoId: repo.repoId,
  697. });
  698. }
  699. }
  700. }
  701. ctx.body = responseFormat.responseFormat(200, "save success!", true);
  702. }
  703. /**
  704. * add recommend repo
  705. *
  706. * @param {Object} ctx request object
  707. * @return {void}
  708. */
  709. async deleteRecommendRepo(ctx) {
  710. let params = ctx.request.body || {};
  711. if (params.key === config.salt) {
  712. for (let item of params.repos) {
  713. await db.repoRecommend.delete({
  714. repoId: item.repoId,
  715. });
  716. }
  717. }
  718. ctx.body = responseFormat.responseFormat(200, "delete success!", true);
  719. }
  720. }
  721. module.exports = RepoController;