@gadmin2n/schematics 0.0.64 → 0.0.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/.dockerignore +2 -2
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile +40 -26
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +30 -4
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/example.prisma +33 -0
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/system.prisma +163 -0
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +70 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Game.ts +6 -6
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/ITActivityDay.ts +70 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Log.ts +2 -2
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Role.ts +2 -2
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env +20 -9
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env.local +1 -0
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.eslintrc.js +1 -0
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.prettierignore +1 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/README.md +2 -18
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/gadmin-cli.json +1 -1
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/migrate-between-pg-schemas.js +1232 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +65 -38
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma/.generator.prisma +4 -3
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +16 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/games.ts +1 -71
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +17 -21
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +278 -0
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/seedDataMngtPages.ts +258 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/users.ts +7 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/alias.config.ts +7 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.controller.ts +151 -11
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.module.ts +29 -13
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.service.ts +151 -12
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/auth.guard.ts +87 -41
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/http-cache.interceptor.ts +21 -0
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/logger.ts +19 -0
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/safe-log.util.ts +176 -0
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/{yufuid.ts → taihu.ts} +49 -34
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/tracing.ts +174 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/trim.pipe.ts +51 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/utils.ts +91 -0
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/woaAuth.ts +25 -12
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +22 -12
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.spec.ts +20 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.ts +190 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.module.ts +10 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.spec.ts +338 -0
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.ts +83 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.spec.ts +20 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.ts +188 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.module.ts +10 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.spec.ts +18 -0
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.ts +83 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.spec.ts +20 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.ts +250 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.module.ts +10 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.spec.ts +18 -0
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.ts +1051 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.spec.ts +20 -0
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.ts +196 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.module.ts +13 -0
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.spec.ts +18 -0
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.ts +219 -0
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.spec.ts +20 -0
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.ts +196 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.module.ts +10 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.spec.ts +18 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.ts +199 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.spec.ts +20 -0
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.ts +210 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.module.ts +12 -0
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.spec.ts +18 -0
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.ts +849 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/roles-refresher.service.ts +133 -0
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.spec.ts +20 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.ts +196 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.module.ts +10 -0
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.spec.ts +18 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.ts +201 -0
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.spec.ts +20 -0
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.ts +196 -0
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.module.ts +10 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.spec.ts +18 -0
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.ts +216 -0
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.spec.ts +20 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +198 -0
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.module.ts +10 -0
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +18 -0
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.ts +104 -0
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/server/start-prod.sh +130 -0
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/server/tsconfig.json +18 -3
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/index.html +19 -0
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +34 -42
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/postcss.config.cjs +6 -0
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +111 -185
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/auditLogProvider.ts +5 -5
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/contexts/color-mode/index.tsx +49 -51
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/custom-avatar.tsx +38 -0
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +4 -0
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/{header/index.tsx → header.tsx} +22 -31
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/index.ts +3 -1
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/layout.tsx +32 -0
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/logo.tsx +19 -0
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +331 -166
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +61 -0
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/pagination-total.tsx +21 -0
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/tags/index.ts +1 -0
  104. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/tags/role-tag.tsx +44 -0
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/text.tsx +74 -0
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +258 -0
  107. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +16 -0
  108. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/enums/audit-log.enum.ts +13 -0
  109. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/enums/index.ts +1 -0
  110. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/login.ts +22 -4
  111. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +211 -0
  112. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useFetchData.ts +33 -0
  113. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useRoles.ts +30 -0
  114. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useUserPageAccess.ts +339 -0
  115. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/i18n.ts +8 -4
  116. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/index.tsx +3 -11
  117. package/dist/lib/application/files/gadmin2-game-angle-demo/web/{public → src}/locales/en/common.json +1 -1
  118. package/dist/lib/application/files/gadmin2-game-angle-demo/web/{public → src}/locales/zh_CN/common.json +1 -1
  119. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.css +3 -0
  120. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.tsx +134 -0
  121. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/create.tsx +113 -0
  122. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/edit.tsx +122 -0
  123. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/index.ts +8 -0
  124. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/index.tsx +6 -0
  125. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/list.tsx +213 -0
  126. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/show.tsx +61 -0
  127. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/AssignRolesModal.tsx +168 -0
  128. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/CreatePageModal.tsx +42 -0
  129. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/EditPageModal.tsx +42 -0
  130. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/PageDetailDrawer.tsx +101 -0
  131. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/PageFormModal.tsx +731 -0
  132. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/create.tsx +113 -0
  133. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/edit.tsx +122 -0
  134. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/hooks/usePageManagement.ts +36 -0
  135. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/index.ts +8 -0
  136. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/index.tsx +6 -0
  137. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +1113 -0
  138. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/queries.ts +17 -0
  139. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/show.tsx +61 -0
  140. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/types.ts +44 -0
  141. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/create.tsx +113 -0
  142. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/edit.tsx +122 -0
  143. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/index.tsx +6 -0
  144. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/list.tsx +243 -0
  145. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/show.tsx +61 -0
  146. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permission-readme/index.tsx +1088 -0
  147. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/CreateModal.tsx +25 -0
  148. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/EditModal.tsx +28 -0
  149. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/ResourceDetailDrawer.tsx +160 -0
  150. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/modal.tsx +202 -0
  151. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/create.tsx +113 -0
  152. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/edit.tsx +122 -0
  153. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/index.ts +9 -0
  154. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/index.tsx +6 -0
  155. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +184 -0
  156. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/queries.ts +10 -0
  157. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/show.tsx +61 -0
  158. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/types.ts +9 -0
  159. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/CreateModal.tsx +30 -0
  160. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/EditModal.tsx +47 -0
  161. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/RoleDetailDrawer.tsx +56 -0
  162. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/modal.tsx +302 -0
  163. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/create.tsx +113 -0
  164. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/edit.tsx +122 -0
  165. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/hooks/useRolePage.ts +35 -0
  166. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/index.ts +8 -0
  167. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/index.tsx +6 -0
  168. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +382 -0
  169. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/queries.ts +8 -0
  170. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/show.tsx +61 -0
  171. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/types.ts +8 -0
  172. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/create.tsx +113 -0
  173. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/edit.tsx +122 -0
  174. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/index.tsx +6 -0
  175. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/list.tsx +243 -0
  176. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/show.tsx +61 -0
  177. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/create.tsx +113 -0
  178. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/edit.tsx +122 -0
  179. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/index.tsx +6 -0
  180. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/list.tsx +243 -0
  181. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/show.tsx +61 -0
  182. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/create-modal.tsx +17 -0
  183. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/edit-modal.tsx +19 -0
  184. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/form-modal.tsx +188 -0
  185. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/index.ts +5 -0
  186. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/role-tag.tsx +48 -0
  187. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/show-drawer.tsx +140 -0
  188. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/create.tsx +113 -0
  189. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/edit.tsx +122 -0
  190. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/index.ts +8 -0
  191. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/index.tsx +6 -0
  192. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +342 -0
  193. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/queries.ts +14 -0
  194. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/show.tsx +61 -0
  195. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +132 -0
  196. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/fc.css +58 -0
  197. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +128 -0
  198. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-drawer.module.css +18 -0
  199. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-page.module.css +21 -0
  200. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/audit-log.ts +1 -0
  201. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/index.ts +3 -0
  202. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/role.ts +7 -0
  203. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/user.ts +1 -0
  204. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/get-name-initials.ts +8 -0
  205. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/get-random-color.ts +27 -0
  206. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/index.ts +2 -0
  207. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/utils.tsx +5 -0
  208. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/vite-env.d.ts +1 -0
  209. package/dist/lib/application/files/gadmin2-game-angle-demo/web/tsconfig.json +3 -3
  210. package/dist/lib/application/files/gadmin2-game-angle-demo/web/vite.config.ts +31 -0
  211. package/package.json +1 -1
  212. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/sample.prisma +0 -65
  213. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Source.ts +0 -76
  214. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Tasklog.ts +0 -76
  215. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/roles.ts +0 -4
  216. package/dist/lib/application/files/gadmin2-game-angle-demo/web/craco.config.js +0 -27
  217. package/dist/lib/application/files/gadmin2-game-angle-demo/web/public/index.html +0 -53
  218. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/styles.ts +0 -10
  219. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/react-app-env.d.ts +0 -1
  220. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/reportWebVitals.ts +0 -15
  221. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.less +0 -79
  222. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/VanillaJSONEditor/{index.js → index.jsx} +0 -0
@@ -1,8 +1,40 @@
1
- import { Injectable } from '@nestjs/common';
2
- import YuFuId, { GAdminTokenName } from './lib/yufuid';
1
+ import { Inject, Injectable } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import * as crypto from 'crypto';
4
+ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
5
+ import * as path from 'path';
6
+ import { Logger } from 'winston';
7
+ import TaihuId, { GAdminTokenName } from './lib/taihu';
3
8
 
4
9
  @Injectable()
5
10
  export class AppService {
11
+ private readonly externalPageSecretKey: string;
12
+ private readonly externalPageExpireSeconds: number;
13
+
14
+ constructor(
15
+ private readonly config: ConfigService,
16
+ @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
17
+ ) {
18
+ this.logger = this.logger.child({
19
+ file: path.basename(__filename),
20
+ });
21
+
22
+ // External Page 配置
23
+ this.externalPageSecretKey =
24
+ this.config.get<string>('EXTERNAL_PAGE_SECRET') ||
25
+ process.env.EXTERNAL_PAGE_SECRET ||
26
+ 'change-this-secret-in-production';
27
+
28
+ this.externalPageExpireSeconds =
29
+ this.config.get<number>('EXTERNAL_PAGE_EXPIRE_SECONDS') || 300;
30
+
31
+ if (this.externalPageSecretKey === 'change-this-secret-in-production') {
32
+ this.logger.warn(
33
+ 'external_page_secret_warning - 使用默认密钥,请在生产环境配置 EXTERNAL_PAGE_SECRET 环境变量',
34
+ );
35
+ }
36
+ }
37
+
6
38
  getHello(): string {
7
39
  return 'Hello World!';
8
40
  }
@@ -10,25 +42,26 @@ export class AppService {
10
42
  async loginCallback(req, res) {
11
43
  const { code, state } = req.query;
12
44
 
13
- const tokenInfo = await YuFuId.getToken(code);
14
- const userInfo = await YuFuId.getUserInfo(tokenInfo.access_token);
45
+ const tokenInfo = await TaihuId.getToken(code);
46
+ const userInfo = await TaihuId.getUserInfo(tokenInfo.access_token);
15
47
 
16
48
  const expiresIn = 7 * 24 * 3600;
17
49
  const { user_id, sub, name, email } = userInfo;
18
50
  const payload = { user_id, sub, name, email };
19
- const token = await YuFuId.sign(payload, expiresIn);
51
+ const token = await TaihuId.sign(payload, expiresIn);
20
52
 
21
53
  // append token param for local development
22
- const url = new URL(state);
23
- if (url.port != '') {
24
- url.searchParams.set(GAdminTokenName, token);
54
+ const redirectUrl = new URL(state);
55
+ const selfUrl = new URL(process.env.TAIHU_CALLBACK || '');
56
+ if (redirectUrl.hostname !== selfUrl.hostname) {
57
+ redirectUrl.searchParams.set(GAdminTokenName, token);
25
58
  }
26
59
 
27
60
  res.cookie(GAdminTokenName, token, {
28
61
  maxAge: expiresIn * 1000,
29
62
  httpOnly: false,
30
63
  });
31
- res.status(302).redirect(url.toString());
64
+ res.status(302).redirect(redirectUrl.toString());
32
65
  }
33
66
 
34
67
  login(req, res) {
@@ -36,15 +69,121 @@ export class AppService {
36
69
 
37
70
  // TODO: verify redirect_url domain
38
71
 
39
- const authorizeUrl = YuFuId.authorize(redirect_url, ui_locales);
72
+ const authorizeUrl = TaihuId.authorize(redirect_url, ui_locales);
40
73
  res.status(302).redirect(authorizeUrl);
41
74
  }
42
75
 
43
76
  logout(req, res) {
44
77
  const { redirect_url } = req.query;
45
78
 
46
- const logoutUrl = YuFuId.logout(redirect_url);
47
- res.cookie(GAdminTokenName);
79
+ const logoutUrl = TaihuId.logout(redirect_url);
80
+ res.cookie(GAdminTokenName, '', { maxAge: 0 });
48
81
  res.status(302).redirect(logoutUrl);
49
82
  }
83
+
84
+ // ============ External Page 功能 ============
85
+
86
+ /**
87
+ * 生成带签名的外部页面 URL
88
+ */
89
+ generateExternalPageSignedUrl(
90
+ domain: string,
91
+ pageCode: string,
92
+ userid: string,
93
+ ): { url: string; expiresIn: number } {
94
+ const timestamp = Date.now();
95
+ const data = `${domain}:${pageCode}:${userid}:${timestamp}`;
96
+
97
+ const signature = crypto
98
+ .createHmac('sha256', this.externalPageSecretKey)
99
+ .update(data)
100
+ .digest('hex');
101
+
102
+ let pagePath: string;
103
+ let pageQuery: string;
104
+
105
+ const queryIndex = pageCode.indexOf('?');
106
+ if (queryIndex !== -1) {
107
+ pagePath = pageCode.substring(0, queryIndex);
108
+ pageQuery = pageCode.substring(queryIndex + 1);
109
+ } else {
110
+ pagePath = pageCode;
111
+ pageQuery = '';
112
+ }
113
+
114
+ const signParams = `_t=${timestamp}&_s=${signature}&_u=${encodeURIComponent(
115
+ userid,
116
+ )}`;
117
+
118
+ let url: string;
119
+ if (pageQuery) {
120
+ url = `https://oit-erp-gtdr.woa.com/${domain}/${pagePath}?${pageQuery}&${signParams}`;
121
+ } else {
122
+ url = `https://oit-erp-gtdr.woa.com/${domain}/${pagePath}?${signParams}`;
123
+ }
124
+
125
+ this.logger.info(
126
+ `external_page_sign_url - 生成签名URL - domain: ${domain}, pageCode: ${pageCode}, userid: ${userid}`,
127
+ );
128
+
129
+ return {
130
+ url,
131
+ expiresIn: this.externalPageExpireSeconds,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * 验证外部页面签名
137
+ */
138
+ verifyExternalPageSignature(
139
+ domain: string,
140
+ pageCode: string,
141
+ userid: string,
142
+ timestamp: string,
143
+ signature: string,
144
+ ): { valid: boolean; reason?: string } {
145
+ const ts = parseInt(timestamp, 10);
146
+ if (isNaN(ts)) {
147
+ return { valid: false, reason: 'invalid timestamp format' };
148
+ }
149
+
150
+ const now = Date.now();
151
+ const age = now - ts;
152
+
153
+ if (age < -5000) {
154
+ return { valid: false, reason: 'timestamp in future' };
155
+ }
156
+
157
+ if (age > this.externalPageExpireSeconds * 1000) {
158
+ return { valid: false, reason: 'signature expired' };
159
+ }
160
+
161
+ const data = `${domain}:${pageCode}:${userid}:${timestamp}`;
162
+ const expectedSignature = crypto
163
+ .createHmac('sha256', this.externalPageSecretKey)
164
+ .update(data)
165
+ .digest('hex');
166
+
167
+ if (signature.length !== expectedSignature.length) {
168
+ return { valid: false, reason: 'invalid signature' };
169
+ }
170
+
171
+ try {
172
+ const signatureBuffer = Buffer.from(signature, 'hex');
173
+ const expectedBuffer = Buffer.from(expectedSignature, 'hex');
174
+
175
+ if (
176
+ !crypto.timingSafeEqual(
177
+ new Uint8Array(signatureBuffer),
178
+ new Uint8Array(expectedBuffer),
179
+ )
180
+ ) {
181
+ return { valid: false, reason: 'invalid signature' };
182
+ }
183
+ } catch {
184
+ return { valid: false, reason: 'invalid signature format' };
185
+ }
186
+
187
+ return { valid: true };
188
+ }
50
189
  }
@@ -12,8 +12,15 @@ import {
12
12
  import { Reflector } from '@nestjs/core';
13
13
  import { ApiSecurity } from '@nestjs/swagger';
14
14
  import { DECORATORS_PREFIX } from '@nestjs/swagger/dist/constants';
15
+ import * as path from 'path';
16
+ import { UserService } from '../modules/user/user.service';
17
+ import { loggerInstance } from './logger';
18
+ import TaiHuId, { GAdminTokenName } from './taihu';
15
19
  import { getIdentity as getWoaIdentity } from './woaAuth';
16
- import YuFuId, { GAdminTokenName } from './yufuid';
20
+
21
+ const logger = loggerInstance.child({
22
+ file: path.basename(__filename),
23
+ });
17
24
 
18
25
  const API_SECURITY = `${DECORATORS_PREFIX}/apiSecurity`;
19
26
  export function ApiKeyAuth(name = 'X-API-KEY') {
@@ -25,83 +32,122 @@ export const AllowUnauthorizedRequest = () =>
25
32
 
26
33
  export const AllowApiKeyRequest = () => SetMetadata('allowApiKeyRequest', true);
27
34
 
28
- async function getIdentity(headers, cookies, key): Promise<UserType> {
29
- // 先验证woa登录态
35
+ async function getIdentity(headers, cookies): Promise<UserType> {
36
+ // 先验证woa登录态 https://iwiki.woa.com/p/4007728669#nodejs-%E7%A4%BA%E4%BE%8B
30
37
  try {
31
- const { staffid, staffname } = await getWoaIdentity(headers, key);
38
+ const { staffid, staffname } = await getWoaIdentity(
39
+ headers,
40
+ process.env.TAIHU_APP_TOKEN || '',
41
+ );
32
42
  if (staffid) {
43
+ logger.info(
44
+ `woa_auth_success - 用户ID: ${staffid}, 用户名: ${staffname}`,
45
+ );
33
46
  return { userid: staffid, username: staffname };
34
47
  }
35
48
  } catch (e) {
36
- console.warn('woa auth failed, try yufu auth...');
49
+ logger.warn(`woa_auth_failed - WOA认证失败: ${e.message}`);
37
50
  }
38
- // 再验证玉符登录态
51
+
52
+ // 再验证taihu登录态 https://iwiki.woa.com/p/4008986665
39
53
  const token = headers[GAdminTokenName] || cookies[GAdminTokenName];
40
- const userInfo = await YuFuId.verify(token);
41
- console.log('yufu auth success', userInfo);
54
+ const userInfo = await TaiHuId.verify(token);
55
+
56
+ logger.info(
57
+ `taihu_auth_success - Taihu认证成功 - 用户ID: ${userInfo.sub}, Token长度: ${
58
+ token ? token.length : 0
59
+ }`,
60
+ );
61
+
42
62
  return { userid: userInfo.sub, username: userInfo.sub };
43
63
  }
44
64
 
45
- // apikey genertor https://codepen.io/corenominal/pen/rxOmMJ
46
- const API_KEYS = [
47
- {
48
- userid: 'steam_session',
49
- username: 'steam_session',
50
- apiKey: '9c5fc474-9e95-480b-b380-5e15f77b5f9f',
51
- },
52
- ];
53
- async function getApikeyIdentity(req): Promise<UserType> {
54
- const apiKey = req.headers['x-api-key'] ?? req.query.api_key;
55
- if (apiKey) {
56
- for (const item of API_KEYS) {
57
- if (item.apiKey === apiKey) {
58
- console.log('apikey auth success:', item.userid);
59
- return { userid: item.userid, username: item.username };
65
+ @Injectable()
66
+ export default class AuthGuard implements CanActivate {
67
+ constructor(private reflector: Reflector, private userService: UserService) {}
68
+
69
+ private async getApikeyIdentity(req): Promise<UserType> {
70
+ const apiKey = req.headers['x-api-key'] ?? req.query.api_key;
71
+
72
+ if (apiKey) {
73
+ const record = await this.userService.findByApiKey(apiKey);
74
+ if (record && record.isActive && record.isApiKey) {
75
+ logger.info(
76
+ `apikey_auth_success - API Key认证成功 - 用户ID: ${
77
+ record.userid
78
+ }, Key前缀: ${apiKey.substring(0, 8)}...`,
79
+ );
80
+ return {
81
+ userid: record.userid,
82
+ username: record.username || record.userid,
83
+ };
60
84
  }
61
85
  }
62
- }
63
- throw new UnauthorizedException(`error apikey: ${apiKey}`);
64
- }
65
86
 
66
- @Injectable()
67
- export default class AuthGuard implements CanActivate {
68
- constructor(private readonly token: string, private reflector: Reflector) {}
87
+ logger.warn(
88
+ `apikey_auth_failed - API Key认证失败 - 提供的Key: ${
89
+ apiKey ? `${apiKey.substring(0, 8)}...` : 'none'
90
+ }`,
91
+ );
92
+
93
+ throw new UnauthorizedException(`error apikey: ${apiKey}`);
94
+ }
69
95
 
70
96
  canActivate(context: ExecutionContext): boolean | Promise<boolean> {
71
97
  const request = context.switchToHttp().getRequest();
98
+ const handler = context.getHandler();
99
+ const controller = context.getClass();
100
+
72
101
  const allowUnauthorizedRequest = this.reflector.get<boolean>(
73
102
  'allowUnauthorizedRequest',
74
- context.getHandler(),
103
+ handler,
75
104
  );
105
+
76
106
  if (allowUnauthorizedRequest) {
107
+ logger.info(
108
+ `auth_bypassed - 认证被绕过 - 端点: ${request.method} ${request.url}, 控制器: ${controller.name}`,
109
+ );
77
110
  return true;
78
111
  }
79
112
 
80
- const allowApiKeyRequest = this.reflector.get<boolean>(
81
- API_SECURITY,
82
- context.getHandler(),
113
+ const allowApiKeyRequest =
114
+ this.reflector.get<boolean>(API_SECURITY, handler) ||
115
+ this.reflector.get<boolean>('allowApiKeyRequest', handler);
116
+
117
+ logger.info(
118
+ `auth_attempt - 认证尝试 - 端点: ${request.method} ${
119
+ request.url
120
+ }, 控制器: ${controller.name}, 允许API Key: ${allowApiKeyRequest}, IP: ${
121
+ request.ip || request.connection.remoteAddress
122
+ }`,
83
123
  );
84
124
 
85
125
  return (
86
126
  allowApiKeyRequest
87
- ? getApikeyIdentity(request)
127
+ ? this.getApikeyIdentity(request)
88
128
  : Promise.reject("don't allow api key request.")
89
129
  )
90
130
  .catch((error) => {
91
- console.warn(error, 'try woa auth...');
92
-
93
- return getIdentity(
94
- request.headers,
95
- request.cookies,
96
- Buffer.from(this.token),
131
+ logger.warn(
132
+ `apikey_fallback - API Key认证失败,回退到WOA/Taihu认证 - 端点: ${request.method} ${request.url}, 错误: ${error.message}`,
97
133
  );
134
+
135
+ return getIdentity(request.headers, request.cookies);
98
136
  })
99
137
  .then((userInfo) => {
100
138
  request.user = userInfo;
139
+
140
+ logger.info(
141
+ `auth_success - 认证成功 - 用户ID: ${userInfo.userid}, 用户名: ${userInfo.username}, 端点: ${request.method} ${request.url}`,
142
+ );
143
+
101
144
  return true;
102
145
  })
103
146
  .catch((error) => {
104
- console.error(error);
147
+ logger.error(
148
+ `auth_failed - 认证失败 - 端点: ${request.method} ${request.url}, 错误: ${error.message}`,
149
+ );
150
+
105
151
  throw new UnauthorizedException(error);
106
152
  });
107
153
  }
@@ -0,0 +1,21 @@
1
+ import { CacheInterceptor } from '@nestjs/cache-manager';
2
+ import { ExecutionContext, Injectable } from '@nestjs/common';
3
+
4
+ /**
5
+ * 扩展 CacheInterceptor,将 POST body 纳入 cache key,
6
+ * 避免不同参数的请求命中同一份缓存。
7
+ * cache key 格式:{url}:{JSON.stringify(body)}
8
+ */
9
+ @Injectable()
10
+ export class HttpCacheInterceptor extends CacheInterceptor {
11
+ trackBy(context: ExecutionContext): string | undefined {
12
+ const request = context.switchToHttp().getRequest();
13
+ const url = request.url as string;
14
+ const body = request.body;
15
+
16
+ const bodyKey =
17
+ body && Object.keys(body).length > 0 ? JSON.stringify(body) : '';
18
+
19
+ return bodyKey ? `${url}:${bodyKey}` : url;
20
+ }
21
+ }
@@ -0,0 +1,19 @@
1
+ import * as winston from 'winston';
2
+
3
+ export const LogFormat = winston.format.combine(
4
+ winston.format.splat(),
5
+ winston.format.timestamp({
6
+ format: 'YYYY-MM-DD HH:mm:ss.SSS',
7
+ }),
8
+ winston.format.errors({ stack: true }),
9
+ winston.format.json(),
10
+ );
11
+
12
+ export const loggerInstance = winston.createLogger({
13
+ level: 'info',
14
+ transports: [
15
+ new winston.transports.Console({
16
+ format: LogFormat,
17
+ }),
18
+ ],
19
+ });
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Safe logging utility to prevent circular structure errors and limit log size
3
+ */
4
+
5
+ const MAX_LOG_SIZE = 10000; // Maximum characters for a single log field
6
+ const MAX_ARRAY_ITEMS = 50; // Maximum array items to log
7
+
8
+ /**
9
+ * Safely stringify an object, handling circular references and limiting size
10
+ * @param obj The object to stringify
11
+ * @param maxDepth Maximum depth to traverse
12
+ * @returns Safe string representation
13
+ */
14
+ export function safeStringify(obj: any, maxDepth = 3): string {
15
+ const seen = new WeakSet();
16
+
17
+ const stringify = (value: any, depth: number): any => {
18
+ // Handle null and undefined
19
+ if (value === null) return null;
20
+ if (value === undefined) return undefined;
21
+
22
+ // Handle primitives
23
+ if (typeof value !== 'object') {
24
+ return value;
25
+ }
26
+
27
+ // Check for circular reference
28
+ if (seen.has(value)) {
29
+ return '[Circular]';
30
+ }
31
+
32
+ // Check depth limit
33
+ if (depth >= maxDepth) {
34
+ return '[Max Depth Reached]';
35
+ }
36
+
37
+ seen.add(value);
38
+
39
+ try {
40
+ // Handle Date
41
+ if (value instanceof Date) {
42
+ return value.toISOString();
43
+ }
44
+
45
+ // Handle Array
46
+ if (Array.isArray(value)) {
47
+ const items = value
48
+ .slice(0, MAX_ARRAY_ITEMS)
49
+ .map((item) => stringify(item, depth + 1));
50
+ if (value.length > MAX_ARRAY_ITEMS) {
51
+ items.push(`[... ${value.length - MAX_ARRAY_ITEMS} more items]`);
52
+ }
53
+ return items;
54
+ }
55
+
56
+ // Handle plain objects
57
+ const result: any = {};
58
+ const keys = Object.keys(value);
59
+
60
+ for (const key of keys) {
61
+ try {
62
+ result[key] = stringify(value[key], depth + 1);
63
+ } catch (error) {
64
+ result[key] = '[Error stringifying]';
65
+ }
66
+ }
67
+
68
+ return result;
69
+ } finally {
70
+ seen.delete(value);
71
+ }
72
+ };
73
+
74
+ try {
75
+ const safeObj = stringify(obj, 0);
76
+ const str = JSON.stringify(safeObj);
77
+
78
+ // Limit total size
79
+ if (str.length > MAX_LOG_SIZE) {
80
+ return (
81
+ str.substring(0, MAX_LOG_SIZE) +
82
+ `... [truncated ${str.length - MAX_LOG_SIZE} chars]`
83
+ );
84
+ }
85
+
86
+ return str;
87
+ } catch (error) {
88
+ return `[Error: ${error.message}]`;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Create a safe log object that can be passed to logger
94
+ * @param data The data to log
95
+ * @returns Safe log object
96
+ */
97
+ export function createSafeLogData(
98
+ data: Record<string, any>,
99
+ ): Record<string, any> {
100
+ const safeData: Record<string, any> = {
101
+ __ROLE_PAGE_RESOURCE__: true, // Unified field for production log collection
102
+ };
103
+
104
+ for (const [key, value] of Object.entries(data)) {
105
+ try {
106
+ // For simple types, use directly
107
+ if (
108
+ value === null ||
109
+ value === undefined ||
110
+ typeof value === 'string' ||
111
+ typeof value === 'number' ||
112
+ typeof value === 'boolean'
113
+ ) {
114
+ safeData[key] = value;
115
+ } else {
116
+ // For complex types, use safe stringify
117
+ const stringified = safeStringify(value);
118
+ try {
119
+ // Try to parse back to object for better log formatting
120
+ safeData[key] = JSON.parse(stringified);
121
+ } catch {
122
+ // If parse fails, use the string
123
+ safeData[key] = stringified;
124
+ }
125
+ }
126
+ } catch (error) {
127
+ safeData[key] = `[Error processing ${key}]`;
128
+ }
129
+ }
130
+
131
+ return safeData;
132
+ }
133
+
134
+ /**
135
+ * Extract only essential fields from database records for logging
136
+ * @param record Database record
137
+ * @param essentialFields Fields to include (defaults to common fields)
138
+ * @returns Simplified record
139
+ */
140
+ export function extractEssentialFields(
141
+ record: any,
142
+ essentialFields?: string[],
143
+ ): any {
144
+ if (!record) return record;
145
+
146
+ const defaultFields = [
147
+ 'id',
148
+ 'name',
149
+ 'code',
150
+ 'type',
151
+ 'status',
152
+ 'createdAt',
153
+ 'updatedAt',
154
+ ];
155
+ const fields = essentialFields || defaultFields;
156
+
157
+ if (Array.isArray(record)) {
158
+ return record.map((item) => extractEssentialFields(item, essentialFields));
159
+ }
160
+
161
+ if (typeof record === 'object') {
162
+ const result: any = {};
163
+ for (const field of fields) {
164
+ if (field in record) {
165
+ result[field] = record[field];
166
+ }
167
+ }
168
+ // Always include id if exists
169
+ if ('id' in record && !result.id) {
170
+ result.id = record.id;
171
+ }
172
+ return result;
173
+ }
174
+
175
+ return record;
176
+ }