@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.
- 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/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
|
+
}
|