From 5231e5f5658ecc1283482cab797a8bedbbcaeba7 Mon Sep 17 00:00:00 2001 From: Kuizuo Date: Fri, 26 Apr 2024 02:12:44 +0800 Subject: [PATCH] chore: init --- .env.example | 33 + .gitignore | 34 + .npmrc | 16 + .nvmrc | 1 + .vscode/project.code-workspace | 27 + .vscode/settings.json | 44 + LICENSE | 21 + README.md | 37 + apps/admin/.editorconfig | 16 + apps/admin/.eslintignore | 8 + apps/admin/.eslintrc.js | 7 + apps/admin/.gitignore | 41 + apps/admin/.prettierignore | 22 + apps/admin/.prettierrc.js | 21 + apps/admin/README.md | 57 + apps/admin/config/config.ts | 151 + apps/admin/config/defaultSettings.ts | 28 + apps/admin/config/oneapi.json | 593 + apps/admin/config/proxy.ts | 42 + apps/admin/config/routes.ts | 95 + apps/admin/jest.config.ts | 23 + apps/admin/jsconfig.json | 11 + apps/admin/mock/listTableList.ts | 176 + apps/admin/mock/notices.ts | 115 + apps/admin/mock/requestRecord.mock.js | 324 + apps/admin/mock/route.ts | 5 + apps/admin/mock/user.ts | 203 + apps/admin/package.json | 95 + apps/admin/public/CNAME | 1 + apps/admin/public/favicon.ico | Bin 0 -> 4286 bytes apps/admin/public/icons/icon-128x128.png | Bin 0 -> 1329 bytes apps/admin/public/icons/icon-192x192.png | Bin 0 -> 1856 bytes apps/admin/public/icons/icon-512x512.png | Bin 0 -> 5082 bytes apps/admin/public/logo.svg | 1 + apps/admin/public/pro_icon.svg | 5 + apps/admin/public/scripts/loading.js | 202 + apps/admin/src/access.ts | 14 + apps/admin/src/app.tsx | 136 + apps/admin/src/components/Footer/index.tsx | 45 + .../src/components/HeaderDropdown/index.tsx | 23 + .../RightContent/AvatarDropdown.tsx | 132 + .../src/components/RightContent/index.tsx | 31 + apps/admin/src/global.less | 53 + apps/admin/src/global.tsx | 91 + apps/admin/src/locales/en-US.ts | 25 + apps/admin/src/locales/en-US/component.ts | 5 + apps/admin/src/locales/en-US/globalHeader.ts | 17 + apps/admin/src/locales/en-US/menu.ts | 52 + apps/admin/src/locales/en-US/pages.ts | 68 + apps/admin/src/locales/en-US/pwa.ts | 6 + apps/admin/src/locales/en-US/settingDrawer.ts | 31 + apps/admin/src/locales/en-US/settings.ts | 60 + apps/admin/src/locales/zh-CN.ts | 25 + apps/admin/src/locales/zh-CN/component.ts | 5 + apps/admin/src/locales/zh-CN/globalHeader.ts | 17 + apps/admin/src/locales/zh-CN/menu.ts | 52 + apps/admin/src/locales/zh-CN/pages.ts | 65 + apps/admin/src/locales/zh-CN/pwa.ts | 6 + apps/admin/src/locales/zh-CN/settingDrawer.ts | 31 + apps/admin/src/locales/zh-CN/settings.ts | 55 + apps/admin/src/manifest.json | 22 + apps/admin/src/pages/404.tsx | 18 + apps/admin/src/pages/Admin.tsx | 45 + apps/admin/src/pages/Welcome.tsx | 164 + apps/admin/src/pages/account/center/index.tsx | 3 + .../src/pages/account/settings/index.tsx | 3 + .../src/pages/dashboard/analysis/index.tsx | 3 + .../src/pages/dashboard/monitor/index.tsx | 3 + .../login/__snapshots__/login.test.tsx.snap | 1114 + apps/admin/src/pages/login/index.tsx | 344 + apps/admin/src/pages/login/login.test.tsx | 96 + apps/admin/src/pages/todo/index.tsx | 338 + apps/admin/src/pages/user/index.tsx | 336 + apps/admin/src/requestErrorConfig.ts | 115 + apps/admin/src/service-worker.js | 65 + apps/admin/src/services/ant-design-pro/api.ts | 42 + .../src/services/ant-design-pro/index.ts | 10 + .../src/services/ant-design-pro/login.ts | 21 + .../src/services/ant-design-pro/typings.d.ts | 45 + apps/admin/src/services/todo/api.ts | 56 + apps/admin/src/services/todo/typings.d.ts | 6 + apps/admin/src/services/user/api.ts | 49 + apps/admin/src/services/user/typings.d.ts | 6 + apps/admin/src/typings.d.ts | 21 + apps/admin/src/utils/auth.ts | 21 + apps/admin/tests/setupTests.jsx | 64 + apps/admin/tsconfig.json | 30 + apps/admin/types/cache/cache.json | 1 + apps/admin/types/cache/login.cache.json | 386 + .../types/cache/mock/login.mock.cache.js | 324 + apps/admin/types/cache/mock/mock.cache.js | 0 apps/admin/types/index.d.ts | 120 + apps/server/.gitignore | 50 + apps/server/.nestjs_repl_history | 0 apps/server/.vscode/launch.json | 21 + apps/server/.vscode/settings.json | 41 + apps/server/ecosystem.config.js | 22 + apps/server/eslint.config.js | 38 + apps/server/nest-cli.json | 9 + apps/server/package.json | 159 + apps/server/src/app.module.ts | 79 + .../assets/templates/verification-code-zh.hbs | 4 + .../assets/templates/verification-code.hbs | 5 + .../src/common/adapters/fastify.adapter.ts | 57 + .../src/common/adapters/socket.adapter.ts | 25 + .../src/common/decorators/bypass.decorator.ts | 10 + .../common/decorators/cron-once.decorator.ts | 19 + .../src/common/decorators/http.decorator.ts | 29 + .../decorators/idempotence.decorator.ts | 15 + .../decorators/protect-keys.decorator.ts | 11 + .../common/decorators/swagger.decorator.ts | 11 + apps/server/src/common/dto/delete.dto.ts | 8 + apps/server/src/common/dto/id.dto.ts | 10 + apps/server/src/common/dto/image.dto.ts | 14 + apps/server/src/common/dto/pager.dto.ts | 23 + .../src/common/exceptions/biz.exception.ts | 43 + .../common/exceptions/not-found.exception.ts | 10 + .../common/filters/any-exception.filter.ts | 94 + .../filters/prisma-client-exception.filter.ts | 121 + .../src/common/filters/ws-exception.filter.ts | 12 + .../interceptors/idempotence.interceptor.ts | 149 + .../interceptors/logging.interceptor.ts | 30 + .../interceptors/timeout.interceptor.ts | 26 + .../interceptors/transform.interceptor.ts | 71 + .../server/src/common/model/response.model.ts | 41 + .../server/src/common/pipes/parse-int.pipe.ts | 18 + .../src/common/pipes/zod-validation.pipe.ts | 21 + apps/server/src/config/app.config.ts | 18 + apps/server/src/config/database.config.ts | 11 + apps/server/src/config/google.config.ts | 10 + apps/server/src/config/index.ts | 7 + apps/server/src/config/mailer.config.ts | 16 + apps/server/src/config/redis.config.ts | 15 + apps/server/src/config/security.config.ts | 12 + apps/server/src/config/sms.config.ts | 13 + apps/server/src/constants/cache.constant.ts | 12 + .../src/constants/error-code.constant.ts | 47 + .../src/constants/event-bus.constant.ts | 6 + apps/server/src/constants/other.constant.ts | 1 + apps/server/src/constants/path.constant.ts | 26 + .../server/src/constants/response.constant.ts | 15 + apps/server/src/constants/system.constant.ts | 2 + apps/server/src/global/env.ts | 67 + apps/server/src/helper/catchError.ts | 5 + apps/server/src/helper/crud/crud.factory.ts | 124 + .../src/helper/get-request-item-id.helper.ts | 8 + apps/server/src/main.ts | 75 + apps/server/src/metadata.ts | 7 + .../src/modules/auth/auth.admin.controller.ts | 59 + apps/server/src/modules/auth/auth.constant.ts | 26 + .../src/modules/auth/auth.controller.ts | 61 + apps/server/src/modules/auth/auth.dto.ts | 16 + apps/server/src/modules/auth/auth.model.ts | 8 + apps/server/src/modules/auth/auth.module.ts | 61 + apps/server/src/modules/auth/auth.service.ts | 65 + apps/server/src/modules/auth/auth.trpc.ts | 59 + .../auth/captcha/captcha.controller.ts | 49 + .../src/modules/auth/captcha/captcha.dto.ts | 20 + .../src/modules/auth/captcha/captcha.model.ts | 3 + .../modules/auth/captcha/captcha.module.ts | 13 + .../modules/auth/captcha/captcha.service.ts | 40 + .../auth/controllers/account.controller.ts | 57 + .../auth/controllers/email.controller.ts | 33 + .../auth/decorators/auth-user.decorator.ts | 17 + .../auth/decorators/public.decorator.ts | 8 + .../src/modules/auth/guards/jwt-auth.guard.ts | 63 + .../src/modules/auth/guards/local.guard.ts | 11 + .../modules/auth/services/token.service.ts | 91 + .../modules/auth/strategies/jwt.strategy.ts | 23 + .../modules/auth/strategies/local.strategy.ts | 24 + apps/server/src/modules/casl/ability.class.ts | 24 + .../src/modules/casl/ability.decorator.ts | 14 + apps/server/src/modules/casl/casl.module.ts | 13 + apps/server/src/modules/casl/casl.service.ts | 47 + .../src/modules/casl/policy.decortor.ts | 14 + apps/server/src/modules/casl/policy.guard.ts | 61 + apps/server/src/modules/file/file.constant.ts | 8 + .../src/modules/file/file.controller.ts | 121 + apps/server/src/modules/file/file.dto.ts | 17 + apps/server/src/modules/file/file.module.ts | 14 + apps/server/src/modules/file/file.service.ts | 86 + apps/server/src/modules/todo/todo.ability.ts | 28 + .../src/modules/todo/todo.controller.ts | 58 + apps/server/src/modules/todo/todo.d.ts | 7 + apps/server/src/modules/todo/todo.dto.ts | 18 + apps/server/src/modules/todo/todo.module.ts | 15 + apps/server/src/modules/todo/todo.service.ts | 110 + apps/server/src/modules/todo/todo.trpc.ts | 86 + .../src/modules/user/dto/account.dto.ts | 15 + .../src/modules/user/dto/password.dto.ts | 19 + .../server/src/modules/user/dto/search.dto.ts | 11 + apps/server/src/modules/user/dto/user.dto.ts | 24 + apps/server/src/modules/user/user.ability.ts | 21 + apps/server/src/modules/user/user.constant.ts | 14 + .../src/modules/user/user.controller.ts | 66 + apps/server/src/modules/user/user.d.ts | 9 + apps/server/src/modules/user/user.module.ts | 23 + .../src/modules/user/user.public.service.ts | 79 + apps/server/src/modules/user/user.service.ts | 201 + apps/server/src/modules/user/user.trpc.ts | 53 + apps/server/src/repl.ts | 12 + apps/server/src/setup-swagger.ts | 47 + apps/server/src/shared/bull/bull.module.ts | 28 + apps/server/src/shared/cache/cache.module.ts | 33 + apps/server/src/shared/cache/cache.service.ts | 89 + .../src/shared/database/database.module.ts | 29 + .../middlewares/snowflake.middleware.ts | 27 + .../src/shared/database/prisma.extension.ts | 95 + .../src/shared/database/snowflake.util.ts | 111 + apps/server/src/shared/helper/cron.service.ts | 16 + .../server/src/shared/helper/helper.module.ts | 47 + .../src/shared/helper/mailer/mailer.module.ts | 49 + .../shared/helper/mailer/mailer.service.ts | 125 + .../server/src/shared/logger/logger.module.ts | 9 + .../src/shared/logger/logger.service.ts | 113 + apps/server/src/shared/redis/redis-subpub.ts | 68 + .../server/src/shared/redis/redis.constant.ts | 1 + apps/server/src/shared/redis/redis.module.ts | 39 + .../server/src/shared/redis/subpub.service.ts | 21 + apps/server/src/shared/trpc/trpc.constant.ts | 1 + apps/server/src/shared/trpc/trpc.context.ts | 13 + apps/server/src/shared/trpc/trpc.decorator.ts | 7 + apps/server/src/shared/trpc/trpc.helper.ts | 13 + apps/server/src/shared/trpc/trpc.instance.ts | 51 + apps/server/src/shared/trpc/trpc.meta.ts | 3 + apps/server/src/shared/trpc/trpc.module.ts | 12 + apps/server/src/shared/trpc/trpc.routes.ts | 9 + apps/server/src/shared/trpc/trpc.service.ts | 176 + apps/server/src/socket/base.gateway.ts | 33 + .../src/socket/business-event.constant.ts | 13 + .../server/src/socket/events/admin.gateway.ts | 36 + apps/server/src/socket/events/web.gateway.ts | 35 + apps/server/src/socket/shared/auth.gateway.ts | 122 + apps/server/src/socket/socket.module.ts | 18 + apps/server/src/utils/captcha.util.ts | 19 + apps/server/src/utils/date.util.ts | 23 + apps/server/src/utils/encrypt.util.ts | 30 + apps/server/src/utils/ip.util.ts | 33 + apps/server/src/utils/is.util.ts | 4 + apps/server/src/utils/prisma.util.ts | 12 + apps/server/src/utils/redis.util.ts | 10 + apps/server/src/utils/schedule.util.ts | 99 + apps/server/src/utils/tool.util.ts | 53 + apps/server/src/utils/zod.util.ts | 63 + apps/server/test/helper/create-e2e-app.ts | 82 + .../server/test/helper/create-service-unit.ts | 34 + apps/server/test/lib/prisma.ts | 5 + apps/server/test/lib/reset-db.ts | 20 + apps/server/test/mock/data/user.data.ts | 18 + apps/server/test/setup.ts | 9 + .../src/modules/account/account.e2e-spec.ts | 48 + .../test/src/modules/auth/auth.e2e-spec.ts | 60 + .../test/src/modules/todo/todo.e2e-spec.ts | 73 + .../src/modules/todo/todo.service.spec.ts | 68 + .../src/modules/user/user.service.spec.ts | 33 + apps/server/tsconfig.build.json | 4 + apps/server/tsconfig.json | 28 + apps/server/types/fastuft.d.ts | 7 + apps/server/types/global.d.ts | 16 + apps/server/types/prisma.d.ts | 1 + apps/server/types/socket.d.ts | 7 + apps/server/vitest.config.ts | 58 + apps/web/.env.example | 1 + apps/web/.gitignore | 36 + apps/web/app/auth/[...nextauth]/route.ts | 2 + apps/web/app/favicon.ico | Bin 0 -> 25931 bytes apps/web/app/globals.css | 18 + apps/web/app/layout.tsx | 30 + apps/web/app/page.tsx | 15 + apps/web/auth.ts | 75 + apps/web/components/auth-components.tsx | 33 + apps/web/components/header.tsx | 12 + apps/web/components/todo-list.tsx | 111 + apps/web/components/user-button.tsx | 17 + apps/web/eslint.config.js | 9 + apps/web/middleware.ts | 46 + apps/web/next.config.mjs | 13 + apps/web/package.json | 34 + apps/web/postcss.config.mjs | 8 + apps/web/public/next.svg | 1 + apps/web/public/vercel.svg | 1 + apps/web/tailwind.config.ts | 20 + apps/web/trpc/react.tsx | 66 + apps/web/tsconfig.json | 27 + eslint.config.ts | 42 + package.json | 51 + packages/database/.gitignore | 5 + packages/database/global.ts | 7 + packages/database/index.d.ts | 3 + packages/database/package.json | 38 + packages/database/schema.prisma | 72 + packages/database/seed.ts | 53 + packages/database/tsconfig.json | 25 + pnpm-lock.yaml | 29826 ++++++++++++++++ pnpm-workspace.yaml | 3 + tsconfig.base.json | 35 + tsconfig.json | 8 + turbo.json | 27 + 298 files changed, 44435 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 .nvmrc create mode 100644 .vscode/project.code-workspace create mode 100644 .vscode/settings.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 apps/admin/.editorconfig create mode 100644 apps/admin/.eslintignore create mode 100644 apps/admin/.eslintrc.js create mode 100644 apps/admin/.gitignore create mode 100644 apps/admin/.prettierignore create mode 100644 apps/admin/.prettierrc.js create mode 100644 apps/admin/README.md create mode 100644 apps/admin/config/config.ts create mode 100644 apps/admin/config/defaultSettings.ts create mode 100644 apps/admin/config/oneapi.json create mode 100644 apps/admin/config/proxy.ts create mode 100644 apps/admin/config/routes.ts create mode 100644 apps/admin/jest.config.ts create mode 100644 apps/admin/jsconfig.json create mode 100644 apps/admin/mock/listTableList.ts create mode 100644 apps/admin/mock/notices.ts create mode 100644 apps/admin/mock/requestRecord.mock.js create mode 100644 apps/admin/mock/route.ts create mode 100644 apps/admin/mock/user.ts create mode 100644 apps/admin/package.json create mode 100644 apps/admin/public/CNAME create mode 100644 apps/admin/public/favicon.ico create mode 100644 apps/admin/public/icons/icon-128x128.png create mode 100644 apps/admin/public/icons/icon-192x192.png create mode 100644 apps/admin/public/icons/icon-512x512.png create mode 100644 apps/admin/public/logo.svg create mode 100644 apps/admin/public/pro_icon.svg create mode 100644 apps/admin/public/scripts/loading.js create mode 100644 apps/admin/src/access.ts create mode 100644 apps/admin/src/app.tsx create mode 100644 apps/admin/src/components/Footer/index.tsx create mode 100644 apps/admin/src/components/HeaderDropdown/index.tsx create mode 100644 apps/admin/src/components/RightContent/AvatarDropdown.tsx create mode 100644 apps/admin/src/components/RightContent/index.tsx create mode 100644 apps/admin/src/global.less create mode 100644 apps/admin/src/global.tsx create mode 100644 apps/admin/src/locales/en-US.ts create mode 100644 apps/admin/src/locales/en-US/component.ts create mode 100644 apps/admin/src/locales/en-US/globalHeader.ts create mode 100644 apps/admin/src/locales/en-US/menu.ts create mode 100644 apps/admin/src/locales/en-US/pages.ts create mode 100644 apps/admin/src/locales/en-US/pwa.ts create mode 100644 apps/admin/src/locales/en-US/settingDrawer.ts create mode 100644 apps/admin/src/locales/en-US/settings.ts create mode 100644 apps/admin/src/locales/zh-CN.ts create mode 100644 apps/admin/src/locales/zh-CN/component.ts create mode 100644 apps/admin/src/locales/zh-CN/globalHeader.ts create mode 100644 apps/admin/src/locales/zh-CN/menu.ts create mode 100644 apps/admin/src/locales/zh-CN/pages.ts create mode 100644 apps/admin/src/locales/zh-CN/pwa.ts create mode 100644 apps/admin/src/locales/zh-CN/settingDrawer.ts create mode 100644 apps/admin/src/locales/zh-CN/settings.ts create mode 100644 apps/admin/src/manifest.json create mode 100644 apps/admin/src/pages/404.tsx create mode 100644 apps/admin/src/pages/Admin.tsx create mode 100644 apps/admin/src/pages/Welcome.tsx create mode 100644 apps/admin/src/pages/account/center/index.tsx create mode 100644 apps/admin/src/pages/account/settings/index.tsx create mode 100644 apps/admin/src/pages/dashboard/analysis/index.tsx create mode 100644 apps/admin/src/pages/dashboard/monitor/index.tsx create mode 100644 apps/admin/src/pages/login/__snapshots__/login.test.tsx.snap create mode 100644 apps/admin/src/pages/login/index.tsx create mode 100644 apps/admin/src/pages/login/login.test.tsx create mode 100644 apps/admin/src/pages/todo/index.tsx create mode 100644 apps/admin/src/pages/user/index.tsx create mode 100644 apps/admin/src/requestErrorConfig.ts create mode 100644 apps/admin/src/service-worker.js create mode 100644 apps/admin/src/services/ant-design-pro/api.ts create mode 100644 apps/admin/src/services/ant-design-pro/index.ts create mode 100644 apps/admin/src/services/ant-design-pro/login.ts create mode 100644 apps/admin/src/services/ant-design-pro/typings.d.ts create mode 100644 apps/admin/src/services/todo/api.ts create mode 100644 apps/admin/src/services/todo/typings.d.ts create mode 100644 apps/admin/src/services/user/api.ts create mode 100644 apps/admin/src/services/user/typings.d.ts create mode 100644 apps/admin/src/typings.d.ts create mode 100644 apps/admin/src/utils/auth.ts create mode 100644 apps/admin/tests/setupTests.jsx create mode 100644 apps/admin/tsconfig.json create mode 100644 apps/admin/types/cache/cache.json create mode 100644 apps/admin/types/cache/login.cache.json create mode 100644 apps/admin/types/cache/mock/login.mock.cache.js create mode 100644 apps/admin/types/cache/mock/mock.cache.js create mode 100644 apps/admin/types/index.d.ts create mode 100644 apps/server/.gitignore create mode 100644 apps/server/.nestjs_repl_history create mode 100644 apps/server/.vscode/launch.json create mode 100644 apps/server/.vscode/settings.json create mode 100644 apps/server/ecosystem.config.js create mode 100644 apps/server/eslint.config.js create mode 100644 apps/server/nest-cli.json create mode 100644 apps/server/package.json create mode 100644 apps/server/src/app.module.ts create mode 100644 apps/server/src/assets/templates/verification-code-zh.hbs create mode 100644 apps/server/src/assets/templates/verification-code.hbs create mode 100644 apps/server/src/common/adapters/fastify.adapter.ts create mode 100644 apps/server/src/common/adapters/socket.adapter.ts create mode 100644 apps/server/src/common/decorators/bypass.decorator.ts create mode 100644 apps/server/src/common/decorators/cron-once.decorator.ts create mode 100644 apps/server/src/common/decorators/http.decorator.ts create mode 100644 apps/server/src/common/decorators/idempotence.decorator.ts create mode 100644 apps/server/src/common/decorators/protect-keys.decorator.ts create mode 100644 apps/server/src/common/decorators/swagger.decorator.ts create mode 100644 apps/server/src/common/dto/delete.dto.ts create mode 100644 apps/server/src/common/dto/id.dto.ts create mode 100644 apps/server/src/common/dto/image.dto.ts create mode 100644 apps/server/src/common/dto/pager.dto.ts create mode 100644 apps/server/src/common/exceptions/biz.exception.ts create mode 100644 apps/server/src/common/exceptions/not-found.exception.ts create mode 100644 apps/server/src/common/filters/any-exception.filter.ts create mode 100644 apps/server/src/common/filters/prisma-client-exception.filter.ts create mode 100644 apps/server/src/common/filters/ws-exception.filter.ts create mode 100644 apps/server/src/common/interceptors/idempotence.interceptor.ts create mode 100644 apps/server/src/common/interceptors/logging.interceptor.ts create mode 100644 apps/server/src/common/interceptors/timeout.interceptor.ts create mode 100644 apps/server/src/common/interceptors/transform.interceptor.ts create mode 100644 apps/server/src/common/model/response.model.ts create mode 100644 apps/server/src/common/pipes/parse-int.pipe.ts create mode 100644 apps/server/src/common/pipes/zod-validation.pipe.ts create mode 100644 apps/server/src/config/app.config.ts create mode 100644 apps/server/src/config/database.config.ts create mode 100644 apps/server/src/config/google.config.ts create mode 100644 apps/server/src/config/index.ts create mode 100644 apps/server/src/config/mailer.config.ts create mode 100644 apps/server/src/config/redis.config.ts create mode 100644 apps/server/src/config/security.config.ts create mode 100644 apps/server/src/config/sms.config.ts create mode 100644 apps/server/src/constants/cache.constant.ts create mode 100644 apps/server/src/constants/error-code.constant.ts create mode 100644 apps/server/src/constants/event-bus.constant.ts create mode 100644 apps/server/src/constants/other.constant.ts create mode 100644 apps/server/src/constants/path.constant.ts create mode 100644 apps/server/src/constants/response.constant.ts create mode 100644 apps/server/src/constants/system.constant.ts create mode 100644 apps/server/src/global/env.ts create mode 100644 apps/server/src/helper/catchError.ts create mode 100644 apps/server/src/helper/crud/crud.factory.ts create mode 100644 apps/server/src/helper/get-request-item-id.helper.ts create mode 100644 apps/server/src/main.ts create mode 100644 apps/server/src/metadata.ts create mode 100644 apps/server/src/modules/auth/auth.admin.controller.ts create mode 100644 apps/server/src/modules/auth/auth.constant.ts create mode 100644 apps/server/src/modules/auth/auth.controller.ts create mode 100644 apps/server/src/modules/auth/auth.dto.ts create mode 100644 apps/server/src/modules/auth/auth.model.ts create mode 100644 apps/server/src/modules/auth/auth.module.ts create mode 100644 apps/server/src/modules/auth/auth.service.ts create mode 100644 apps/server/src/modules/auth/auth.trpc.ts create mode 100644 apps/server/src/modules/auth/captcha/captcha.controller.ts create mode 100644 apps/server/src/modules/auth/captcha/captcha.dto.ts create mode 100644 apps/server/src/modules/auth/captcha/captcha.model.ts create mode 100644 apps/server/src/modules/auth/captcha/captcha.module.ts create mode 100644 apps/server/src/modules/auth/captcha/captcha.service.ts create mode 100644 apps/server/src/modules/auth/controllers/account.controller.ts create mode 100644 apps/server/src/modules/auth/controllers/email.controller.ts create mode 100644 apps/server/src/modules/auth/decorators/auth-user.decorator.ts create mode 100644 apps/server/src/modules/auth/decorators/public.decorator.ts create mode 100644 apps/server/src/modules/auth/guards/jwt-auth.guard.ts create mode 100644 apps/server/src/modules/auth/guards/local.guard.ts create mode 100644 apps/server/src/modules/auth/services/token.service.ts create mode 100644 apps/server/src/modules/auth/strategies/jwt.strategy.ts create mode 100644 apps/server/src/modules/auth/strategies/local.strategy.ts create mode 100644 apps/server/src/modules/casl/ability.class.ts create mode 100644 apps/server/src/modules/casl/ability.decorator.ts create mode 100644 apps/server/src/modules/casl/casl.module.ts create mode 100644 apps/server/src/modules/casl/casl.service.ts create mode 100644 apps/server/src/modules/casl/policy.decortor.ts create mode 100644 apps/server/src/modules/casl/policy.guard.ts create mode 100644 apps/server/src/modules/file/file.constant.ts create mode 100644 apps/server/src/modules/file/file.controller.ts create mode 100644 apps/server/src/modules/file/file.dto.ts create mode 100644 apps/server/src/modules/file/file.module.ts create mode 100644 apps/server/src/modules/file/file.service.ts create mode 100644 apps/server/src/modules/todo/todo.ability.ts create mode 100644 apps/server/src/modules/todo/todo.controller.ts create mode 100644 apps/server/src/modules/todo/todo.d.ts create mode 100644 apps/server/src/modules/todo/todo.dto.ts create mode 100644 apps/server/src/modules/todo/todo.module.ts create mode 100644 apps/server/src/modules/todo/todo.service.ts create mode 100644 apps/server/src/modules/todo/todo.trpc.ts create mode 100644 apps/server/src/modules/user/dto/account.dto.ts create mode 100644 apps/server/src/modules/user/dto/password.dto.ts create mode 100644 apps/server/src/modules/user/dto/search.dto.ts create mode 100644 apps/server/src/modules/user/dto/user.dto.ts create mode 100644 apps/server/src/modules/user/user.ability.ts create mode 100644 apps/server/src/modules/user/user.constant.ts create mode 100644 apps/server/src/modules/user/user.controller.ts create mode 100644 apps/server/src/modules/user/user.d.ts create mode 100644 apps/server/src/modules/user/user.module.ts create mode 100644 apps/server/src/modules/user/user.public.service.ts create mode 100644 apps/server/src/modules/user/user.service.ts create mode 100644 apps/server/src/modules/user/user.trpc.ts create mode 100644 apps/server/src/repl.ts create mode 100644 apps/server/src/setup-swagger.ts create mode 100644 apps/server/src/shared/bull/bull.module.ts create mode 100644 apps/server/src/shared/cache/cache.module.ts create mode 100644 apps/server/src/shared/cache/cache.service.ts create mode 100644 apps/server/src/shared/database/database.module.ts create mode 100644 apps/server/src/shared/database/middlewares/snowflake.middleware.ts create mode 100644 apps/server/src/shared/database/prisma.extension.ts create mode 100644 apps/server/src/shared/database/snowflake.util.ts create mode 100644 apps/server/src/shared/helper/cron.service.ts create mode 100644 apps/server/src/shared/helper/helper.module.ts create mode 100644 apps/server/src/shared/helper/mailer/mailer.module.ts create mode 100644 apps/server/src/shared/helper/mailer/mailer.service.ts create mode 100644 apps/server/src/shared/logger/logger.module.ts create mode 100644 apps/server/src/shared/logger/logger.service.ts create mode 100644 apps/server/src/shared/redis/redis-subpub.ts create mode 100644 apps/server/src/shared/redis/redis.constant.ts create mode 100644 apps/server/src/shared/redis/redis.module.ts create mode 100644 apps/server/src/shared/redis/subpub.service.ts create mode 100644 apps/server/src/shared/trpc/trpc.constant.ts create mode 100644 apps/server/src/shared/trpc/trpc.context.ts create mode 100644 apps/server/src/shared/trpc/trpc.decorator.ts create mode 100644 apps/server/src/shared/trpc/trpc.helper.ts create mode 100644 apps/server/src/shared/trpc/trpc.instance.ts create mode 100644 apps/server/src/shared/trpc/trpc.meta.ts create mode 100644 apps/server/src/shared/trpc/trpc.module.ts create mode 100644 apps/server/src/shared/trpc/trpc.routes.ts create mode 100644 apps/server/src/shared/trpc/trpc.service.ts create mode 100644 apps/server/src/socket/base.gateway.ts create mode 100644 apps/server/src/socket/business-event.constant.ts create mode 100644 apps/server/src/socket/events/admin.gateway.ts create mode 100644 apps/server/src/socket/events/web.gateway.ts create mode 100644 apps/server/src/socket/shared/auth.gateway.ts create mode 100644 apps/server/src/socket/socket.module.ts create mode 100644 apps/server/src/utils/captcha.util.ts create mode 100644 apps/server/src/utils/date.util.ts create mode 100644 apps/server/src/utils/encrypt.util.ts create mode 100644 apps/server/src/utils/ip.util.ts create mode 100644 apps/server/src/utils/is.util.ts create mode 100644 apps/server/src/utils/prisma.util.ts create mode 100644 apps/server/src/utils/redis.util.ts create mode 100644 apps/server/src/utils/schedule.util.ts create mode 100644 apps/server/src/utils/tool.util.ts create mode 100644 apps/server/src/utils/zod.util.ts create mode 100644 apps/server/test/helper/create-e2e-app.ts create mode 100644 apps/server/test/helper/create-service-unit.ts create mode 100644 apps/server/test/lib/prisma.ts create mode 100644 apps/server/test/lib/reset-db.ts create mode 100644 apps/server/test/mock/data/user.data.ts create mode 100644 apps/server/test/setup.ts create mode 100644 apps/server/test/src/modules/account/account.e2e-spec.ts create mode 100644 apps/server/test/src/modules/auth/auth.e2e-spec.ts create mode 100644 apps/server/test/src/modules/todo/todo.e2e-spec.ts create mode 100644 apps/server/test/src/modules/todo/todo.service.spec.ts create mode 100644 apps/server/test/src/modules/user/user.service.spec.ts create mode 100644 apps/server/tsconfig.build.json create mode 100644 apps/server/tsconfig.json create mode 100644 apps/server/types/fastuft.d.ts create mode 100644 apps/server/types/global.d.ts create mode 100644 apps/server/types/prisma.d.ts create mode 100644 apps/server/types/socket.d.ts create mode 100644 apps/server/vitest.config.ts create mode 100644 apps/web/.env.example create mode 100644 apps/web/.gitignore create mode 100644 apps/web/app/auth/[...nextauth]/route.ts create mode 100644 apps/web/app/favicon.ico create mode 100644 apps/web/app/globals.css create mode 100644 apps/web/app/layout.tsx create mode 100644 apps/web/app/page.tsx create mode 100644 apps/web/auth.ts create mode 100644 apps/web/components/auth-components.tsx create mode 100644 apps/web/components/header.tsx create mode 100644 apps/web/components/todo-list.tsx create mode 100644 apps/web/components/user-button.tsx create mode 100644 apps/web/eslint.config.js create mode 100644 apps/web/middleware.ts create mode 100644 apps/web/next.config.mjs create mode 100644 apps/web/package.json create mode 100644 apps/web/postcss.config.mjs create mode 100644 apps/web/public/next.svg create mode 100644 apps/web/public/vercel.svg create mode 100644 apps/web/tailwind.config.ts create mode 100644 apps/web/trpc/react.tsx create mode 100644 apps/web/tsconfig.json create mode 100644 eslint.config.ts create mode 100644 package.json create mode 100644 packages/database/.gitignore create mode 100644 packages/database/global.ts create mode 100644 packages/database/index.d.ts create mode 100644 packages/database/package.json create mode 100644 packages/database/schema.prisma create mode 100644 packages/database/seed.ts create mode 100644 packages/database/tsconfig.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 tsconfig.base.json create mode 100644 tsconfig.json create mode 100644 turbo.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..816ad86 --- /dev/null +++ b/.env.example @@ -0,0 +1,33 @@ +# app +APP_NAME=nest-trpc-prisma-starter +APP_PORT=5001 +APP_BASE_URL=http://localhost:5001 + +# logger +LOGGER_LEVEL=debug + +# security +JWT_SECRET=mySecretKey123 +JWT_EXPIRE=7d + +# db +POSTGRES_USER=postgres +POSTGRES_PASSWORD= +POSTGRES_DB=demo + +DB_HOST=127.0.0.1 +DB_PORT=5432 + +DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:${DB_PORT}/${POSTGRES_DB}?schema=public + +# redis +REDIS_PORT=6379 +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD= +REDIS_DB=0 + +# smtp +SMTP_HOST=smtp.qq.com +SMTP_PORT=465 +SMTP_USER= +SMTP_PASS= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d2ad93 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# compiled output +dist +node_modules + +# testing +coverage + +# OS +.DS_Store + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# local env files +.env +.env.* +!.env.example + +# log files +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# turbo +.turbo \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..501d622 --- /dev/null +++ b/.npmrc @@ -0,0 +1,16 @@ +public-hoist-pattern[]=*fastify* +public-hoist-pattern[]=fastify +public-hoist-pattern[]=*@typescript-eslint* +public-hoist-pattern[]=*eslint* +public-hoist-pattern[]=*prettier* +public-hoist-pattern[]=*socket.io* +public-hoist-pattern[]=*jsonwebtoken* +public-hoist-pattern[]=@nestjs* +public-hoist-pattern[]=*prisma* + +registry=https://registry.npmjs.org + +node-linker=hoisted +shamefully-hoist=true +enable-pre-post-scripts=true +strict-peer-dependencies=false diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a3d2332 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20.11 \ No newline at end of file diff --git a/.vscode/project.code-workspace b/.vscode/project.code-workspace new file mode 100644 index 0000000..b295565 --- /dev/null +++ b/.vscode/project.code-workspace @@ -0,0 +1,27 @@ +{ + "folders": [ + { + "name": "ROOT", + "path": "../" + }, + { + "name": "server", + "path": "../apps/server" + }, + { + "name": "admin", + "path": "../apps/admin" + }, + { + "name": "web", + "path": "../apps/web" + }, + { + "name": "database", + "path": "../packages/database" + }, + ], + "settings": { + "testing.automaticallyOpenPeekView": "never" + }, + } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7d337a9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,44 @@ +{ + // Enable the ESlint flat config support + "eslint.experimental.useFlatConfig": true, + + // Disable the default formatter, use eslint instead + "prettier.enable": false, + "editor.formatOnSave": false, + + // Auto fix + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" + }, + + // Silent the stylistic rules in you IDE, but still auto fix them + "eslint.rules.customizations": [ + { "rule": "style/*", "severity": "off" }, + { "rule": "format/*", "severity": "off" }, + { "rule": "*-indent", "severity": "off" }, + { "rule": "*-spacing", "severity": "off" }, + { "rule": "*-spaces", "severity": "off" }, + { "rule": "*-order", "severity": "off" }, + { "rule": "*-dangle", "severity": "off" }, + { "rule": "*-newline", "severity": "off" }, + { "rule": "*quotes", "severity": "off" }, + { "rule": "*semi", "severity": "off" } + ], + + // Enable eslint for all supported languages + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "yaml", + "toml" + ], + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..245dda3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 kuizuo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9297a29 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Nest + Prisma + tRPC + Zod + +这是一个使用 Nest.js、Prisma、tRPC 和 Zod 构建的现代化全栈应用程序的示例项目。 + +## 🔧 技术栈 + +- Server + - NestJS + - tRPC + - Prisma + - Zod +- Web + - Next.js +- Admin + - Ant Design Pro + +## 📄 使用说明 + +撰写中... + +## TODO + +- [ ] 升级到 Trpc 11 +- [ ] 升级 Tanstack Query 5 +- [ ] Nest.js 集成 Auth.js + +## 参考 + +http://blog.innei.ren/nestjs-with-trpc-and-dependency-injection + +https://www.tomray.dev/nestjs-nextjs-trpc + +## 📝License + +[MIT](./LICENSE) + +Copyright (c) 2024 Kuizuo \ No newline at end of file diff --git a/apps/admin/.editorconfig b/apps/admin/.editorconfig new file mode 100644 index 0000000..7e3649a --- /dev/null +++ b/apps/admin/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/apps/admin/.eslintignore b/apps/admin/.eslintignore new file mode 100644 index 0000000..8336e93 --- /dev/null +++ b/apps/admin/.eslintignore @@ -0,0 +1,8 @@ +/lambda/ +/scripts +/config +.history +public +dist +.umi +mock \ No newline at end of file diff --git a/apps/admin/.eslintrc.js b/apps/admin/.eslintrc.js new file mode 100644 index 0000000..3ac39ef --- /dev/null +++ b/apps/admin/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: [require.resolve('@umijs/lint/dist/config/eslint')], + globals: { + page: true, + REACT_APP_ENV: true, + }, +}; diff --git a/apps/admin/.gitignore b/apps/admin/.gitignore new file mode 100644 index 0000000..0fb3670 --- /dev/null +++ b/apps/admin/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +**/node_modules +# roadhog-api-doc ignore +/src/utils/request-temp.js +_roadhog-api-doc + +# production +/dist + +# misc +.DS_Store +npm-debug.log* +yarn-error.log + +/coverage +.idea +yarn.lock +package-lock.json +*bak +.vscode + + +# visual studio code +.history +*.log +functions/* +.temp/** + +# umi +.umi +.umi-production +.umi-test + +# screenshot +screenshot +.firebase +.eslintcache + +build diff --git a/apps/admin/.prettierignore b/apps/admin/.prettierignore new file mode 100644 index 0000000..7999ccd --- /dev/null +++ b/apps/admin/.prettierignore @@ -0,0 +1,22 @@ +**/*.svg +.umi +.umi-production +/dist +.dockerignore +.DS_Store +.eslintignore +*.png +*.toml +docker +.editorconfig +Dockerfile* +.gitignore +.prettierignore +LICENSE +.eslintcache +*.lock +yarn-error.log +.history +CNAME +/build +/public diff --git a/apps/admin/.prettierrc.js b/apps/admin/.prettierrc.js new file mode 100644 index 0000000..3447a1a --- /dev/null +++ b/apps/admin/.prettierrc.js @@ -0,0 +1,21 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', + printWidth: 100, + proseWrap: 'never', + endOfLine: 'lf', + overrides: [ + { + files: '.prettierrc', + options: { + parser: 'json', + }, + }, + { + files: 'document.ejs', + options: { + parser: 'html', + }, + }, + ], +}; diff --git a/apps/admin/README.md b/apps/admin/README.md new file mode 100644 index 0000000..4c89a72 --- /dev/null +++ b/apps/admin/README.md @@ -0,0 +1,57 @@ +# Ant Design Pro + +This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use. + +## Environment Prepare + +Install `node_modules`: + +```bash +npm install +``` + +or + +```bash +yarn +``` + +## Provided Scripts + +Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test. + +Scripts provided in `package.json`. It's safe to modify or add additional script: + +### Start project + +```bash +npm start +``` + +### Build project + +```bash +npm run build +``` + +### Check code style + +```bash +npm run lint +``` + +You can also use script to auto fix some lint error: + +```bash +npm run lint:fix +``` + +### Test code + +```bash +npm test +``` + +## More + +You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro). diff --git a/apps/admin/config/config.ts b/apps/admin/config/config.ts new file mode 100644 index 0000000..9492b28 --- /dev/null +++ b/apps/admin/config/config.ts @@ -0,0 +1,151 @@ +// https://umijs.org/config/ +import { defineConfig } from '@umijs/max'; +import { join } from 'path'; +import defaultSettings from './defaultSettings'; +import proxy from './proxy'; +import routes from './routes'; + +const { REACT_APP_ENV = 'dev' } = process.env; + +export default defineConfig({ + /** + * @name 开启 hash 模式 + * @description 让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。 + * @doc https://umijs.org/docs/api/config#hash + */ + hash: true, + + /** + * @name 兼容性设置 + * @description 设置 ie11 不一定完美兼容,需要检查自己使用的所有依赖 + * @doc https://umijs.org/docs/api/config#targets + */ + // targets: { + // ie: 11, + // }, + /** + * @name 路由的配置,不在路由中引入的文件不会编译 + * @description 只支持 path,component,routes,redirect,wrappers,title 的配置 + * @doc https://umijs.org/docs/guides/routes + */ + // umi routes: https://umijs.org/docs/routing + routes, + /** + * @name 主题的配置 + * @description 虽然叫主题,但是其实只是 less 的变量设置 + * @doc antd的主题设置 https://ant.design/docs/react/customize-theme-cn + * @doc umi 的theme 配置 https://umijs.org/docs/api/config#theme + */ + theme: { + // 如果不想要 configProvide 动态设置主题需要把这个设置为 default + // 只有设置为 variable, 才能使用 configProvide 动态设置主色调 + 'root-entry-name': 'variable', + }, + /** + * @name moment 的国际化配置 + * @description 如果对国际化没有要求,打开之后能减少js的包大小 + * @doc https://umijs.org/docs/api/config#ignoremomentlocale + */ + ignoreMomentLocale: true, + /** + * @name 代理配置 + * @description 可以让你的本地服务器代理到你的服务器上,这样你就可以访问服务器的数据了 + * @see 要注意以下 代理只能在本地开发时使用,build 之后就无法使用了。 + * @doc 代理介绍 https://umijs.org/docs/guides/proxy + * @doc 代理配置 https://umijs.org/docs/api/config#proxy + */ + proxy: proxy[REACT_APP_ENV as keyof typeof proxy], + /** + * @name 快速热更新配置 + * @description 一个不错的热更新组件,更新时可以保留 state + */ + fastRefresh: true, + //============== 以下都是max的插件配置 =============== + /** + * @name 数据流插件 + * @@doc https://umijs.org/docs/max/data-flow + */ + model: {}, + /** + * 一个全局的初始数据流,可以用它在插件之间共享数据 + * @description 可以用来存放一些全局的数据,比如用户信息,或者一些全局的状态,全局初始状态在整个 Umi 项目的最开始创建。 + * @doc https://umijs.org/docs/max/data-flow#%E5%85%A8%E5%B1%80%E5%88%9D%E5%A7%8B%E7%8A%B6%E6%80%81 + */ + initialState: {}, + /** + * @name layout 插件 + * @doc https://umijs.org/docs/max/layout-menu + */ + title: 'Admin', + layout: { + locale: true, + ...defaultSettings, + }, + /** + * @name moment2dayjs 插件 + * @description 将项目中的 moment 替换为 dayjs + * @doc https://umijs.org/docs/max/moment2dayjs + */ + moment2dayjs: { + preset: 'antd', + plugins: ['duration'], + }, + /** + * @name 国际化插件 + * @doc https://umijs.org/docs/max/i18n + */ + locale: { + // default zh-CN + default: 'zh-CN', + antd: true, + // default true, when it is true, will use `navigator.language` overwrite default + baseNavigator: true, + }, + /** + * @name antd 插件 + * @description 内置了 babel import 插件 + * @doc https://umijs.org/docs/max/antd#antd + */ + antd: {}, + /** + * @name 网络请求配置 + * @description 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。 + * @doc https://umijs.org/docs/max/request + */ + request: { + dataField: 'data', + }, + /** + * @name 权限插件 + * @description 基于 initialState 的权限插件,必须先打开 initialState + * @doc https://umijs.org/docs/max/access + */ + access: {}, + /** + * @name 中额外的 script + * @description 配置 中额外的 script + */ + headScripts: [ + // 解决首次加载时白屏的问题 + { src: '/scripts/loading.js', async: true }, + ], + //================ pro 插件配置 ================= + presets: ['umi-presets-pro'], + /** + * @name openAPI 插件的配置 + * @description 基于 openapi 的规范生成serve 和mock,能减少很多样板代码 + * @doc https://pro.ant.design/zh-cn/docs/openapi/ + */ + // openAPI: [ + // { + // requestLibPath: "import { request } from '@umijs/max'", + // schemaPath: 'http://localhost:6001/api-docs-json', + // projectName: 'swagger', + // mock: false, + // }, + // ], + mfsu: { + strategy: 'normal', + }, + requestRecord: {}, +}); diff --git a/apps/admin/config/defaultSettings.ts b/apps/admin/config/defaultSettings.ts new file mode 100644 index 0000000..9ab1f72 --- /dev/null +++ b/apps/admin/config/defaultSettings.ts @@ -0,0 +1,28 @@ +import { ProLayoutProps } from '@ant-design/pro-components'; + +/** + * @name + */ +const Settings: ProLayoutProps & { + pwa?: boolean; + logo?: string; +} = { + navTheme: 'light', + // 拂晓蓝 + colorPrimary: '#1890ff', + layout: 'mix', + contentWidth: 'Fluid', + fixedHeader: false, + fixSiderbar: true, + colorWeak: false, + title: 'Admin', + pwa: true, + logo: '/logo.svg', + iconfontUrl: '', + token: { + // 参见ts声明,demo 见文档,通过token 修改样式 + //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F + }, +}; + +export default Settings; diff --git a/apps/admin/config/oneapi.json b/apps/admin/config/oneapi.json new file mode 100644 index 0000000..c77d988 --- /dev/null +++ b/apps/admin/config/oneapi.json @@ -0,0 +1,593 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Ant Design Pro", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost:8000/" + }, + { + "url": "https://localhost:8000/" + } + ], + "paths": { + "/api/currentUser": { + "get": { + "tags": ["api"], + "description": "获取当前的用户", + "operationId": "currentUser", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrentUser" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "x-swagger-router-controller": "api" + }, + "/api/login/captcha": { + "post": { + "description": "发送验证码", + "operationId": "getFakeCaptcha", + "tags": ["login"], + "parameters": [ + { + "name": "phone", + "in": "query", + "description": "手机号", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FakeCaptcha" + } + } + } + } + } + } + }, + "/api/login/outLogin": { + "post": { + "description": "登录接口", + "operationId": "outLogin", + "tags": ["login"], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "x-swagger-router-controller": "api" + }, + "/api/login/account": { + "post": { + "tags": ["login"], + "description": "登录接口", + "operationId": "login", + "requestBody": { + "description": "登录系统", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginParams" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginResult" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "x-swagger-router-controller": "api" + }, + "/api/notices": { + "summary": "getNotices", + "description": "NoticeIconItem", + "get": { + "tags": ["api"], + "operationId": "getNotices", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NoticeIconList" + } + } + } + } + } + } + }, + "/api/rule": { + "get": { + "tags": ["rule"], + "description": "获取规则列表", + "operationId": "rule", + "parameters": [ + { + "name": "current", + "in": "query", + "description": "当前的页码", + "schema": { + "type": "number" + } + }, + { + "name": "pageSize", + "in": "query", + "description": "页面的容量", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuleList" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": ["rule"], + "description": "新建规则", + "operationId": "addRule", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuleListItem" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": ["rule"], + "description": "新建规则", + "operationId": "updateRule", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuleListItem" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": ["rule"], + "description": "删除规则", + "operationId": "removeRule", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "x-swagger-router-controller": "api" + }, + "/swagger": { + "x-swagger-pipe": "swagger_raw" + } + }, + "components": { + "schemas": { + "CurrentUser": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "userid": { + "type": "string" + }, + "email": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "title": { + "type": "string" + }, + "group": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "notifyCount": { + "type": "integer", + "format": "int32" + }, + "unreadCount": { + "type": "integer", + "format": "int32" + }, + "country": { + "type": "string" + }, + "access": { + "type": "string" + }, + "geographic": { + "type": "object", + "properties": { + "province": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "city": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "key": { + "type": "string" + } + } + } + } + }, + "address": { + "type": "string" + }, + "phone": { + "type": "string" + } + } + }, + "LoginResult": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "type": { + "type": "string" + }, + "currentAuthority": { + "type": "string" + } + } + }, + "PageParams": { + "type": "object", + "properties": { + "current": { + "type": "number" + }, + "pageSize": { + "type": "number" + } + } + }, + "RuleListItem": { + "type": "object", + "properties": { + "key": { + "type": "integer", + "format": "int32" + }, + "disabled": { + "type": "boolean" + }, + "href": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "callNo": { + "type": "integer", + "format": "int32" + }, + "status": { + "type": "integer", + "format": "int32" + }, + "updatedAt": { + "type": "string", + "format": "datetime" + }, + "createdAt": { + "type": "string", + "format": "datetime" + }, + "progress": { + "type": "integer", + "format": "int32" + } + } + }, + "RuleList": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RuleListItem" + } + }, + "total": { + "type": "integer", + "description": "列表的内容总数", + "format": "int32" + }, + "success": { + "type": "boolean" + } + } + }, + "FakeCaptcha": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "status": { + "type": "string" + } + } + }, + "LoginParams": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "autoLogin": { + "type": "boolean" + }, + "type": { + "type": "string" + } + } + }, + "ErrorResponse": { + "required": ["errorCode"], + "type": "object", + "properties": { + "errorCode": { + "type": "string", + "description": "业务约定的错误码" + }, + "errorMessage": { + "type": "string", + "description": "业务上的错误信息" + }, + "success": { + "type": "boolean", + "description": "业务上的请求是否成功" + } + } + }, + "NoticeIconList": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NoticeIconItem" + } + }, + "total": { + "type": "integer", + "description": "列表的内容总数", + "format": "int32" + }, + "success": { + "type": "boolean" + } + } + }, + "NoticeIconItemType": { + "title": "NoticeIconItemType", + "description": "已读未读列表的枚举", + "type": "string", + "properties": {}, + "enum": ["notification", "message", "event"] + }, + "NoticeIconItem": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "extra": { + "type": "string", + "format": "any" + }, + "key": { "type": "string" }, + "read": { + "type": "boolean" + }, + "avatar": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "datetime": { + "type": "string", + "format": "date" + }, + "description": { + "type": "string" + }, + "type": { + "extensions": { + "x-is-enum": true + }, + "$ref": "#/components/schemas/NoticeIconItemType" + } + } + } + } + } +} diff --git a/apps/admin/config/proxy.ts b/apps/admin/config/proxy.ts new file mode 100644 index 0000000..5a93395 --- /dev/null +++ b/apps/admin/config/proxy.ts @@ -0,0 +1,42 @@ +/** + * @name 代理的配置 + * @see 在生产环境 代理是无法生效的,所以这里没有生产环境的配置 + * ------------------------------- + * The agent cannot take effect in the production environment + * so there is no configuration of the production environment + * For details, please see + * https://pro.ant.design/docs/deploy + * + * @doc https://umijs.org/docs/guides/proxy + */ +export default { + // 如果需要自定义本地开发服务器 请取消注释按需调整 + dev: { + // localhost:8000/api/** -> https://preview.pro.ant.design/api/** + '/api/': { + // 要代理的地址 + target: 'http://localhost:5001', + changeOrigin: true, + }, + }, + + /** + * @name 详细的代理配置 + * @doc https://github.com/chimurai/http-proxy-middleware + */ + // test: { + // // localhost:8000/api/** -> https://preview.pro.ant.design/api/** + // '/api/': { + // target: 'https://proapi.azurewebsites.net', + // changeOrigin: true, + // pathRewrite: { '^': '' }, + // }, + // }, + // pre: { + // '/api/': { + // target: 'your pre url', + // changeOrigin: true, + // pathRewrite: { '^': '' }, + // }, + // }, +}; diff --git a/apps/admin/config/routes.ts b/apps/admin/config/routes.ts new file mode 100644 index 0000000..ea4b7c1 --- /dev/null +++ b/apps/admin/config/routes.ts @@ -0,0 +1,95 @@ +/** + * @name umi 的路由配置 + * @description 只支持 path,component,routes,redirect,wrappers,name,icon 的配置 + * @param path path 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。 + * @param component 配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages 开始找起。 + * @param routes 配置子路由,通常在需要为多个路径增加 layout 组件时使用。 + * @param redirect 配置路由跳转 + * @param wrappers 配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验 + * @param name 配置路由的标题,默认读取国际化文件 menu.ts 中 menu.xxxx 的值,如配置 name 为 login,则读取 menu.ts 中 menu.login 的取值作为标题 + * @param icon 配置路由的图标,取值参考 https://ant.design/components/icon-cn, 注意去除风格后缀和大小写,如想要配置图标为 则取值应为 stepBackward 或 StepBackward,如想要配置图标为 则取值应为 user 或者 User + * @doc https://umijs.org/docs/guides/routes + */ +export default [ + { + layout: false, + name: 'login', + path: '/login', + component: './login', + }, + { + path: '/dashboard', + name: 'dashboard', + icon: 'dashboard', + routes: [ + { + path: '/dashboard', + redirect: '/dashboard/analysis', + }, + { + name: 'analysis', + icon: 'smile', + path: '/dashboard/analysis', + component: './dashboard/analysis', + }, + { + name: 'monitor', + icon: 'smile', + path: '/dashboard/monitor', + component: './dashboard/monitor', + }, + // { + // name: 'workplace', + // icon: 'smile', + // path: '/dashboard/workplace', + // component: './dashboard/workplace', + // }, + ], + }, + { + name: 'account', + icon: 'user', + path: '/account', + routes: [ + { + path: '/account', + redirect: '/account/center', + }, + { + name: 'center', + icon: 'smile', + path: '/account/center', + component: './account/center', + }, + { + name: 'settings', + icon: 'smile', + path: '/account/settings', + component: './account/settings', + }, + ], + }, + { + name: 'Todo管理', + icon: 'user', + path: '/todo', + component: './todo', + access: 'canAdmin', + }, + { + name: '用户管理', + icon: 'user', + path: '/user', + component: './user', + access: 'canAdmin', + }, + { + path: '/', + redirect: '/dashboard/analysis', + }, + { + path: '*', + layout: false, + component: './404', + }, +]; diff --git a/apps/admin/jest.config.ts b/apps/admin/jest.config.ts new file mode 100644 index 0000000..1de2a1a --- /dev/null +++ b/apps/admin/jest.config.ts @@ -0,0 +1,23 @@ +import { configUmiAlias, createConfig } from '@umijs/max/test'; + +export default async () => { + const config = await configUmiAlias({ + ...createConfig({ + target: 'browser', + }), + }); + + console.log(); + return { + ...config, + testEnvironmentOptions: { + ...(config?.testEnvironmentOptions || {}), + url: 'http://localhost:8000', + }, + setupFiles: [...(config.setupFiles || []), './tests/setupTests.jsx'], + globals: { + ...config.globals, + localStorage: null, + }, + }; +}; diff --git a/apps/admin/jsconfig.json b/apps/admin/jsconfig.json new file mode 100644 index 0000000..197bee5 --- /dev/null +++ b/apps/admin/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/apps/admin/mock/listTableList.ts b/apps/admin/mock/listTableList.ts new file mode 100644 index 0000000..35ec3ce --- /dev/null +++ b/apps/admin/mock/listTableList.ts @@ -0,0 +1,176 @@ +import { Request, Response } from 'express'; +import moment from 'moment'; +import { parse } from 'url'; + +// mock tableListDataSource +const genList = (current: number, pageSize: number) => { + const tableListDataSource: API.RuleListItem[] = []; + + for (let i = 0; i < pageSize; i += 1) { + const index = (current - 1) * 10 + i; + tableListDataSource.push({ + key: index, + disabled: i % 6 === 0, + href: 'https://ant.design', + avatar: [ + 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + ][i % 2], + name: `TradeCode ${index}`, + owner: '曲丽丽', + desc: '这是一段描述', + callNo: Math.floor(Math.random() * 1000), + status: Math.floor(Math.random() * 10) % 4, + updatedAt: moment().format('YYYY-MM-DD'), + createdAt: moment().format('YYYY-MM-DD'), + progress: Math.ceil(Math.random() * 100), + }); + } + tableListDataSource.reverse(); + return tableListDataSource; +}; + +let tableListDataSource = genList(1, 100); + +function getRule(req: Request, res: Response, u: string) { + let realUrl = u; + if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { + realUrl = req.url; + } + const { current = 1, pageSize = 10 } = req.query; + const params = parse(realUrl, true).query as unknown as API.PageParams & + API.RuleListItem & { + sorter: any; + filter: any; + }; + + let dataSource = [...tableListDataSource].slice( + ((current as number) - 1) * (pageSize as number), + (current as number) * (pageSize as number), + ); + if (params.sorter) { + const sorter = JSON.parse(params.sorter); + dataSource = dataSource.sort((prev, next) => { + let sortNumber = 0; + (Object.keys(sorter) as Array).forEach((key) => { + let nextSort = next?.[key] as number; + let preSort = prev?.[key] as number; + if (sorter[key] === 'descend') { + if (preSort - nextSort > 0) { + sortNumber += -1; + } else { + sortNumber += 1; + } + return; + } + if (preSort - nextSort > 0) { + sortNumber += 1; + } else { + sortNumber += -1; + } + }); + return sortNumber; + }); + } + if (params.filter) { + const filter = JSON.parse(params.filter as any) as { + [key: string]: string[]; + }; + if (Object.keys(filter).length > 0) { + dataSource = dataSource.filter((item) => { + return (Object.keys(filter) as Array).some((key) => { + if (!filter[key]) { + return true; + } + if (filter[key].includes(`${item[key]}`)) { + return true; + } + return false; + }); + }); + } + } + + if (params.name) { + dataSource = dataSource.filter((data) => data?.name?.includes(params.name || '')); + } + const result = { + data: dataSource, + total: tableListDataSource.length, + success: true, + pageSize, + current: parseInt(`${params.current}`, 10) || 1, + }; + + return res.json(result); +} + +function postRule(req: Request, res: Response, u: string, b: Request) { + let realUrl = u; + if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { + realUrl = req.url; + } + + const body = (b && b.body) || req.body; + const { method, name, desc, key } = body; + + switch (method) { + /* eslint no-case-declarations:0 */ + case 'delete': + tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1); + break; + case 'post': + (() => { + const i = Math.ceil(Math.random() * 10000); + const newRule: API.RuleListItem = { + key: tableListDataSource.length, + href: 'https://ant.design', + avatar: [ + 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + ][i % 2], + name, + owner: '曲丽丽', + desc, + callNo: Math.floor(Math.random() * 1000), + status: Math.floor(Math.random() * 10) % 2, + updatedAt: moment().format('YYYY-MM-DD'), + createdAt: moment().format('YYYY-MM-DD'), + progress: Math.ceil(Math.random() * 100), + }; + tableListDataSource.unshift(newRule); + return res.json(newRule); + })(); + return; + + case 'update': + (() => { + let newRule = {}; + tableListDataSource = tableListDataSource.map((item) => { + if (item.key === key) { + newRule = { ...item, desc, name }; + return { ...item, desc, name }; + } + return item; + }); + return res.json(newRule); + })(); + return; + default: + break; + } + + const result = { + list: tableListDataSource, + pagination: { + total: tableListDataSource.length, + }, + }; + + res.json(result); +} + +export default { + 'GET /api/rule': getRule, + 'POST /api/rule': postRule, +}; diff --git a/apps/admin/mock/notices.ts b/apps/admin/mock/notices.ts new file mode 100644 index 0000000..616c921 --- /dev/null +++ b/apps/admin/mock/notices.ts @@ -0,0 +1,115 @@ +import { Request, Response } from 'express'; + +const getNotices = (req: Request, res: Response) => { + res.json({ + data: [ + { + id: '000000001', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/MSbDR4FR2MUAAAAAAAAAAAAAFl94AQBr', + title: '你收到了 14 份新周报', + datetime: '2017-08-09', + type: 'notification', + }, + { + id: '000000002', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/hX-PTavYIq4AAAAAAAAAAAAAFl94AQBr', + title: '你推荐的 曲妮妮 已通过第三轮面试', + datetime: '2017-08-08', + type: 'notification', + }, + { + id: '000000003', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/jHX5R5l3QjQAAAAAAAAAAAAAFl94AQBr', + title: '这种模板可以区分多种通知类型', + datetime: '2017-08-07', + read: true, + type: 'notification', + }, + { + id: '000000004', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Wr4mQqx6jfwAAAAAAAAAAAAAFl94AQBr', + title: '左侧图标用于区分不同的类型', + datetime: '2017-08-07', + type: 'notification', + }, + { + id: '000000005', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Mzj_TbcWUj4AAAAAAAAAAAAAFl94AQBr', + title: '内容不要超过两行字,超出时自动截断', + datetime: '2017-08-07', + type: 'notification', + }, + { + id: '000000006', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/eXLzRbPqQE4AAAAAAAAAAAAAFl94AQBr', + title: '曲丽丽 评论了你', + description: '描述信息描述信息描述信息', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000007', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/w5mRQY2AmEEAAAAAAAAAAAAAFl94AQBr', + title: '朱偏右 回复了你', + description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000008', + avatar: + 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/wPadR5M9918AAAAAAAAAAAAAFl94AQBr', + title: '标题', + description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000009', + title: '任务名称', + description: '任务需要在 2017-01-12 20:00 前启动', + extra: '未开始', + status: 'todo', + type: 'event', + }, + { + id: '000000010', + title: '第三方紧急代码变更', + description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', + extra: '马上到期', + status: 'urgent', + type: 'event', + }, + { + id: '000000011', + title: '信息安全考试', + description: '指派竹尔于 2017-01-09 前完成更新并发布', + extra: '已耗时 8 天', + status: 'doing', + type: 'event', + }, + { + id: '000000012', + title: 'ABCD 版本发布', + description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', + extra: '进行中', + status: 'processing', + type: 'event', + }, + ], + }); +}; + +export default { + 'GET /api/notices': getNotices, +}; diff --git a/apps/admin/mock/requestRecord.mock.js b/apps/admin/mock/requestRecord.mock.js new file mode 100644 index 0000000..6c59e19 --- /dev/null +++ b/apps/admin/mock/requestRecord.mock.js @@ -0,0 +1,324 @@ +module.exports = { + 'GET /api/currentUser': { + data: { + name: 'Serati Ma', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png', + userid: '00000001', + email: 'antdesign@alipay.com', + signature: '海纳百川,有容乃大', + title: '交互专家', + group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED', + tags: [ + { key: '0', label: '很有想法的' }, + { key: '1', label: '专注设计' }, + { key: '2', label: '辣~' }, + { key: '3', label: '大长腿' }, + { key: '4', label: '川妹子' }, + { key: '5', label: '海纳百川' }, + ], + notifyCount: 12, + unreadCount: 11, + country: 'China', + geographic: { + province: { label: '浙江省', key: '330000' }, + city: { label: '杭州市', key: '330100' }, + }, + address: '西湖区工专路 77 号', + phone: '0752-268888888', + }, + }, + 'GET /api/rule': { + data: [ + { + key: 99, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 99', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 503, + status: '0', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 81, + }, + { + key: 98, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 98', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 164, + status: '0', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 12, + }, + { + key: 97, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 97', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 174, + status: '1', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 81, + }, + { + key: 96, + disabled: true, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 96', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 914, + status: '0', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 7, + }, + { + key: 95, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 95', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 698, + status: '2', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 82, + }, + { + key: 94, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 94', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 488, + status: '1', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 14, + }, + { + key: 93, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 93', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 580, + status: '2', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 77, + }, + { + key: 92, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 92', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 244, + status: '3', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 58, + }, + { + key: 91, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 91', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 959, + status: '0', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 66, + }, + { + key: 90, + disabled: true, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 90', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 958, + status: '0', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 72, + }, + { + key: 89, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 89', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 301, + status: '2', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 2, + }, + { + key: 88, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 88', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 277, + status: '1', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 12, + }, + { + key: 87, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 87', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 810, + status: '1', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 82, + }, + { + key: 86, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 86', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 780, + status: '3', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 22, + }, + { + key: 85, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 85', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 705, + status: '3', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 12, + }, + { + key: 84, + disabled: true, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 84', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 203, + status: '0', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 79, + }, + { + key: 83, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 83', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 491, + status: '2', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 59, + }, + { + key: 82, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 82', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 73, + status: '0', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 100, + }, + { + key: 81, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + name: 'TradeCode 81', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 406, + status: '3', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 61, + }, + { + key: 80, + disabled: false, + href: 'https://ant.design', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + name: 'TradeCode 80', + owner: '曲丽丽', + desc: '这是一段描述', + callNo: 112, + status: '2', + updatedAt: '2022-12-06T05:00:57.040Z', + createdAt: '2022-12-06T05:00:57.040Z', + progress: 20, + }, + ], + total: 100, + success: true, + pageSize: 20, + current: 1, + }, + 'POST /api/login/outLogin': { data: {}, success: true }, + 'POST /api/login/account': { + status: 'ok', + type: 'account', + currentAuthority: 'admin', + }, +}; diff --git a/apps/admin/mock/route.ts b/apps/admin/mock/route.ts new file mode 100644 index 0000000..418d10f --- /dev/null +++ b/apps/admin/mock/route.ts @@ -0,0 +1,5 @@ +export default { + '/api/auth_routes': { + '/form/advanced-form': { authority: ['admin', 'user'] }, + }, +}; diff --git a/apps/admin/mock/user.ts b/apps/admin/mock/user.ts new file mode 100644 index 0000000..75edd34 --- /dev/null +++ b/apps/admin/mock/user.ts @@ -0,0 +1,203 @@ +import { Request, Response } from 'express'; + +const waitTime = (time: number = 100) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(true); + }, time); + }); +}; + +async function getFakeCaptcha(req: Request, res: Response) { + await waitTime(2000); + return res.json('captcha-xxx'); +} + +const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env; + +/** + * 当前用户的权限,如果为空代表没登录 + * current user access, if is '', user need login + * 如果是 pro 的预览,默认是有权限的 + */ +let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : ''; + +const getAccess = () => { + return access; +}; + +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + // 支持值为 Object 和 Array + 'GET /api/currentUser': (req: Request, res: Response) => { + if (!getAccess()) { + res.status(401).send({ + data: { + isLogin: false, + }, + errorCode: '401', + errorMessage: '请先登录!', + success: true, + }); + return; + } + res.send({ + success: true, + data: { + name: 'Serati Ma', + avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png', + userid: '00000001', + email: 'antdesign@alipay.com', + signature: '海纳百川,有容乃大', + title: '交互专家', + group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED', + tags: [ + { + key: '0', + label: '很有想法的', + }, + { + key: '1', + label: '专注设计', + }, + { + key: '2', + label: '辣~', + }, + { + key: '3', + label: '大长腿', + }, + { + key: '4', + label: '川妹子', + }, + { + key: '5', + label: '海纳百川', + }, + ], + notifyCount: 12, + unreadCount: 11, + country: 'China', + access: getAccess(), + geographic: { + province: { + label: '浙江省', + key: '330000', + }, + city: { + label: '杭州市', + key: '330100', + }, + }, + address: '西湖区工专路 77 号', + phone: '0752-268888888', + }, + }); + }, + // GET POST 可省略 + 'GET /api/users': [ + { + key: '1', + name: 'John Brown', + age: 32, + address: 'New York No. 1 Lake Park', + }, + { + key: '2', + name: 'Jim Green', + age: 42, + address: 'London No. 1 Lake Park', + }, + { + key: '3', + name: 'Joe Black', + age: 32, + address: 'Sidney No. 1 Lake Park', + }, + ], + 'POST /api/login/account': async (req: Request, res: Response) => { + const { password, username, type } = req.body; + await waitTime(2000); + if (password === 'ant.design' && username === 'admin') { + res.send({ + status: 'ok', + type, + currentAuthority: 'admin', + }); + access = 'admin'; + return; + } + if (password === 'ant.design' && username === 'user') { + res.send({ + status: 'ok', + type, + currentAuthority: 'user', + }); + access = 'user'; + return; + } + if (type === 'mobile') { + res.send({ + status: 'ok', + type, + currentAuthority: 'admin', + }); + access = 'admin'; + return; + } + + res.send({ + status: 'error', + type, + currentAuthority: 'guest', + }); + access = 'guest'; + }, + 'POST /api/login/outLogin': (req: Request, res: Response) => { + access = ''; + res.send({ data: {}, success: true }); + }, + 'POST /api/register': (req: Request, res: Response) => { + res.send({ status: 'ok', currentAuthority: 'user', success: true }); + }, + 'GET /api/500': (req: Request, res: Response) => { + res.status(500).send({ + timestamp: 1513932555104, + status: 500, + error: 'error', + message: 'error', + path: '/base/category/list', + }); + }, + 'GET /api/404': (req: Request, res: Response) => { + res.status(404).send({ + timestamp: 1513932643431, + status: 404, + error: 'Not Found', + message: 'No message available', + path: '/base/category/list/2121212', + }); + }, + 'GET /api/403': (req: Request, res: Response) => { + res.status(403).send({ + timestamp: 1513932555104, + status: 403, + error: 'Forbidden', + message: 'Forbidden', + path: '/base/category/list', + }); + }, + 'GET /api/401': (req: Request, res: Response) => { + res.status(401).send({ + timestamp: 1513932555104, + status: 401, + error: 'Unauthorized', + message: 'Unauthorized', + path: '/base/category/list', + }); + }, + + 'GET /api/login/captcha': getFakeCaptcha, +}; diff --git a/apps/admin/package.json b/apps/admin/package.json new file mode 100644 index 0000000..a0822aa --- /dev/null +++ b/apps/admin/package.json @@ -0,0 +1,95 @@ +{ + "name": "admin", + "version": "0.1.0", + "private": true, + "description": "An out-of-box UI solution for enterprise applications", + "scripts": { + "analyze": "cross-env ANALYZE=1 max build", + "build": "max build", + "deploy": "npm run build", + "dev": "npm run start:dev", + "i18n-remove": "pro i18n-remove --locale=zh-CN --write", + "postinstall": "max setup", + "jest": "jest", + "lint": "npm run lint:js && npm run lint:prettier && npm run tsc", + "lint-staged": "lint-staged", + "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", + "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ", + "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", + "lint:prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto", + "openapi": "max openapi", + "prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"", + "preview": "npm run build && max preview --port 8000", + "record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login", + "serve": "umi-serve", + "start": "cross-env UMI_ENV=dev max dev", + "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev", + "start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev", + "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", + "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", + "test": "jest", + "test:coverage": "npm run jest -- --coverage", + "test:update": "npm run jest -- -u", + "tsc": "tsc --noEmit" + }, + "lint-staged": { + "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", + "**/*.{js,jsx,tsx,ts,less,md,json}": [ + "prettier --write" + ] + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 10" + ], + "dependencies": { + "@ant-design/icons": "^4.8.0", + "@ant-design/pro-components": "^2.3.57", + "@ant-design/use-emotion-css": "1.0.4", + "@umijs/route-utils": "^2.2.2", + "antd": "^5.2.2", + "classnames": "^2.3.2", + "js-cookie": "^3.0.5", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "omit.js": "^2.0.2", + "rc-menu": "^9.8.2", + "rc-util": "^5.27.2", + "react": "^18.2.0", + "react-dev-inspector": "^1.8.4", + "react-dom": "^18.2.0", + "react-helmet-async": "^1.3.0" + }, + "devDependencies": { + "@ant-design/pro-cli": "^2.1.5", + "@testing-library/react": "^13.4.0", + "@types/express": "^4.17.17", + "@types/history": "^4.7.11", + "@types/jest": "^29.4.0", + "@types/lodash": "^4.14.191", + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "@types/react-helmet": "^6.1.6", + "@umijs/fabric": "^2.14.1", + "@umijs/lint": "^4.0.52", + "@umijs/max": "^4.0.52", + "cross-env": "^7.0.3", + "eslint": "^8.34.0", + "express": "^4.18.2", + "gh-pages": "^3.2.3", + "husky": "^7.0.4", + "jest": "^29.4.3", + "jest-environment-jsdom": "^29.4.3", + "lint-staged": "^10.5.4", + "mockjs": "^1.1.0", + "prettier": "^2.8.4", + "swagger-ui-dist": "^4.15.5", + "ts-node": "^10.9.1", + "typescript": "^5.3.0", + "umi-presets-pro": "^2.0.2" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/apps/admin/public/CNAME b/apps/admin/public/CNAME new file mode 100644 index 0000000..30c2d4d --- /dev/null +++ b/apps/admin/public/CNAME @@ -0,0 +1 @@ +preview.pro.ant.design \ No newline at end of file diff --git a/apps/admin/public/favicon.ico b/apps/admin/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e2e93252988b5bed120ddeb168eb577bf0caa881 GIT binary patch literal 4286 zcmeHLO(?8k6#ga#Gs=ufvobTd=q@y~nOLdYlqL%nQY4WqC`)z}3y~cqOAAF4VkQd~ z^3$wrC}~D1l%!eMp!|gA&U5sN?~b{2>)yNg-s#Lc-+9k_p7Y<=XqqYcDJs(Vt%dBG z=F~Lpdy*pd!Y zozZT}udJ-#| zqiAVq0sT@kAMMobbULxOw+F6{j*egO8CQ?TgTuo^+}_@zrlv;l$*-)e#PRVl?(Xgo zi9`?zg>ZFsh0DuJ)Ya9!von7LPN@OfP9IfH_4tet&&I~a#O{TK1<3*LO-xJ( zZ*Fc5%v&m4#Z&mZySs8{XJ-fV^Yh@{p`ju8d_J)w6+U%`!(qAC+uJK~qg_o+O|QC` zTVg0Y{jSHqzP^^&G0t4^cwF+$`egj?@9$AtTMLuPB>7!lUKY8&zP?xd=H_PM(U;HS ztNi!(_oJes0<5X6tu4{BxVR{FO)_x3UauFkv$I%RTSHe@7dAIHF+4mhc?|>t(!XbCXVQZk z8yje7Xh2R*&WCwdw&?N4$H!4$Uyq)i9_;V$3;y)^-LKhmkv{Z#(CP^-7Wn# zI5-H>z`y`nTU%u=f7Tbh-mg5fPT0G8`Wx6^vD1(>-r0;AFlyjWYC!!D@UQlt)%}4^ literal 0 HcmV?d00001 diff --git a/apps/admin/public/icons/icon-128x128.png b/apps/admin/public/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..48d0e2339a60a637b94319c65e8654289b4f4b6c GIT binary patch literal 1329 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

*C7T$?5!b= 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 28 Copy 5Created with Sketch. \ No newline at end of file diff --git a/apps/admin/public/pro_icon.svg b/apps/admin/public/pro_icon.svg new file mode 100644 index 0000000..e075b78 --- /dev/null +++ b/apps/admin/public/pro_icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/admin/public/scripts/loading.js b/apps/admin/public/scripts/loading.js new file mode 100644 index 0000000..c1ced54 --- /dev/null +++ b/apps/admin/public/scripts/loading.js @@ -0,0 +1,202 @@ +/** + * loading 占位 + * 解决首次加载时白屏的问题 + */ + (function () { + const _root = document.querySelector('#root'); + if (_root && _root.innerHTML === '') { + _root.innerHTML = ` + + +

+
+
+ + + + + + +
+
+
+ 正在加载资源 +
+
+ 初次加载资源可能需要较多时间 请耐心等待 +
+
+ `; + } +})(); diff --git a/apps/admin/src/access.ts b/apps/admin/src/access.ts new file mode 100644 index 0000000..89f9b33 --- /dev/null +++ b/apps/admin/src/access.ts @@ -0,0 +1,14 @@ +const Role = { + Admin: 'Admin', + User: 'User' +} + +/** + * @see https://umijs.org/zh-CN/plugins/plugin-access + * */ +export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) { + const { currentUser } = initialState ?? {}; + return { + canAdmin: currentUser && currentUser.role === Role.Admin, + }; +} diff --git a/apps/admin/src/app.tsx b/apps/admin/src/app.tsx new file mode 100644 index 0000000..5024099 --- /dev/null +++ b/apps/admin/src/app.tsx @@ -0,0 +1,136 @@ +import Footer from '@/components/Footer'; +import { Question, SelectLang } from '@/components/RightContent'; +import { LinkOutlined } from '@ant-design/icons'; +import type { Settings as LayoutSettings } from '@ant-design/pro-components'; +import { SettingDrawer } from '@ant-design/pro-components'; +import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max'; +import { history, Link } from '@umijs/max'; +import defaultSettings from '../config/defaultSettings'; +import { errorConfig } from './requestErrorConfig'; +import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; +import React from 'react'; +import { AvatarDropdown, AvatarName } from './components/RightContent/AvatarDropdown'; +const isDev = process.env.NODE_ENV === 'development'; +const loginPath = '/login'; + +/** + * @see https://umijs.org/zh-CN/plugins/plugin-initial-state + * */ +export async function getInitialState(): Promise<{ + settings?: Partial; + currentUser?: API.CurrentUser; + loading?: boolean; + fetchUserInfo?: () => Promise; +}> { + const fetchUserInfo = async () => { + try { + const response = await queryCurrentUser({ + skipErrorHandler: true, + }); + return response.data; + } catch (error) { + history.push(loginPath); + } + return undefined; + }; + // 如果不是登录页面,执行 + const { location } = history; + if (location.pathname !== loginPath) { + const currentUser = await fetchUserInfo(); + return { + fetchUserInfo, + currentUser, + settings: defaultSettings as Partial, + }; + } + return { + fetchUserInfo, + settings: defaultSettings as Partial, + }; +} + +// ProLayout 支持的api https://procomponents.ant.design/components/layout +export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => { + return { + actionsRender: () => [, ], + avatarProps: { + src: initialState?.currentUser?.avatar, + title: , + render: (_, avatarChildren) => { + return {avatarChildren}; + }, + }, + // waterMarkProps: { + // content: initialState?.currentUser?.username, + // }, + // footerRender: () =>