@gadmin2n/schematics 0.0.63 → 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.
- package/dist/lib/application/files/gadmin2-game-angle-demo/.dockerignore +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile +40 -26
- package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +30 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/example.prisma +33 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/system.prisma +163 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +70 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Game.ts +6 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/ITActivityDay.ts +70 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Log.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Role.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env +20 -9
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env.local +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.eslintrc.js +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.prettierignore +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/README.md +2 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/gadmin-cli.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/migrate-between-pg-schemas.js +1232 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +65 -38
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma/.generator.prisma +4 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/games.ts +1 -71
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +17 -21
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +278 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/seedDataMngtPages.ts +258 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/users.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/alias.config.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.controller.ts +151 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.module.ts +29 -13
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.service.ts +151 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/auth.guard.ts +87 -41
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/http-cache.interceptor.ts +21 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/logger.ts +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/safe-log.util.ts +176 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/{yufuid.ts → taihu.ts} +49 -34
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/tracing.ts +174 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/trim.pipe.ts +51 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/utils.ts +91 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/woaAuth.ts +25 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +22 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.ts +190 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.spec.ts +338 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.ts +83 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.ts +188 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.ts +83 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.ts +250 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.ts +1051 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.module.ts +13 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.ts +219 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.ts +199 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.ts +210 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.module.ts +12 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.ts +849 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/roles-refresher.service.ts +133 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.ts +201 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.ts +216 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +198 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.ts +104 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/start-prod.sh +130 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/tsconfig.json +18 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/index.html +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +34 -42
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/postcss.config.cjs +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +111 -185
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/auditLogProvider.ts +5 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/contexts/color-mode/index.tsx +49 -51
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/custom-avatar.tsx +38 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +4 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/{header/index.tsx → header.tsx} +22 -31
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/index.ts +3 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/layout.tsx +32 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/logo.tsx +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +331 -166
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/pagination-total.tsx +21 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/tags/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/tags/role-tag.tsx +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/text.tsx +74 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +258 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/enums/audit-log.enum.ts +13 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/enums/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/form.tsx +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/login.ts +22 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +211 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useFetchData.ts +33 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useRoles.ts +30 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useUserPageAccess.ts +339 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/i18n.ts +8 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/index.tsx +3 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/{public → src}/locales/en/common.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/{public → src}/locales/zh_CN/common.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.css +3 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.tsx +134 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/list.tsx +213 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/AssignRolesModal.tsx +168 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/CreatePageModal.tsx +42 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/EditPageModal.tsx +42 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/PageDetailDrawer.tsx +101 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/PageFormModal.tsx +731 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/hooks/usePageManagement.ts +36 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +1113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/queries.ts +17 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/types.ts +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/list.tsx +243 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permission-readme/index.tsx +1088 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/CreateModal.tsx +25 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/EditModal.tsx +28 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/ResourceDetailDrawer.tsx +160 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/modal.tsx +202 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/index.ts +9 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +184 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/queries.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/types.ts +9 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/CreateModal.tsx +30 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/EditModal.tsx +47 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/RoleDetailDrawer.tsx +56 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/modal.tsx +302 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/hooks/useRolePage.ts +35 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +382 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/queries.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/types.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/list.tsx +243 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/list.tsx +243 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/create-modal.tsx +17 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/edit-modal.tsx +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/form-modal.tsx +188 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/index.ts +5 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/role-tag.tsx +48 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/show-drawer.tsx +140 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +342 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/queries.ts +14 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +132 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/fc.css +58 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +128 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-drawer.module.css +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-page.module.css +21 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/audit-log.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/index.ts +3 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/role.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/user.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/get-name-initials.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/get-random-color.ts +27 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/index.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/utils.tsx +5 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/vite-env.d.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/tsconfig.json +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/vite.config.ts +31 -0
- package/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/sample.prisma +0 -65
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Source.ts +0 -76
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Tasklog.ts +0 -76
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/roles.ts +0 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/craco.config.js +0 -27
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/public/index.html +0 -53
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/styles.ts +0 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/react-app-env.d.ts +0 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/reportWebVitals.ts +0 -15
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.less +0 -79
- /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
|
|
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
|
|
14
|
-
const userInfo = await
|
|
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
|
|
51
|
+
const token = await TaihuId.sign(payload, expiresIn);
|
|
20
52
|
|
|
21
53
|
// append token param for local development
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
41
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
apiKey
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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 =
|
|
81
|
-
API_SECURITY,
|
|
82
|
-
|
|
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
|
-
|
|
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
|
-
|
|
147
|
+
logger.error(
|
|
148
|
+
`auth_failed - 认证失败 - 端点: ${request.method} ${request.url}, 错误: ${error.message}`,
|
|
149
|
+
);
|
|
150
|
+
|
|
105
151
|
throw new UnauthorizedException(error);
|
|
106
152
|
});
|
|
107
153
|
}
|
package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/http-cache.interceptor.ts
ADDED
|
@@ -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
|
+
}
|