@qlover/create-app 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/config/IOCIdentifier.ts +2 -2
  5. package/dist/templates/next-app/config/Identifier/common/common.ts +14 -0
  6. package/dist/templates/next-app/config/Identifier/pages/index.ts +1 -0
  7. package/dist/templates/next-app/config/Identifier/pages/page.about.ts +20 -0
  8. package/dist/templates/next-app/config/common.ts +1 -1
  9. package/dist/templates/next-app/config/cookies.ts +23 -0
  10. package/dist/templates/next-app/config/i18n/AboutI18n.ts +14 -0
  11. package/dist/templates/next-app/config/i18n/i18nConfig.ts +3 -1
  12. package/dist/templates/next-app/config/i18n/index.ts +1 -0
  13. package/dist/templates/next-app/config/i18n/loginI18n.ts +8 -0
  14. package/dist/templates/next-app/config/theme.ts +4 -0
  15. package/dist/templates/next-app/eslint.config.mjs +4 -1
  16. package/dist/templates/next-app/next.config.ts +1 -0
  17. package/dist/templates/next-app/package.json +13 -4
  18. package/dist/templates/next-app/public/locales/en.json +5 -0
  19. package/dist/templates/next-app/public/locales/zh.json +5 -0
  20. package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +37 -0
  21. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +30 -6
  22. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +1 -1
  23. package/dist/templates/next-app/src/app/[locale]/layout.tsx +47 -10
  24. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
  25. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +22 -10
  26. package/dist/templates/next-app/src/app/[locale]/page.tsx +23 -8
  27. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +21 -9
  28. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +7 -28
  29. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +7 -34
  30. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +12 -34
  31. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +7 -26
  32. package/dist/templates/next-app/src/app/api/admin/users/route.ts +14 -33
  33. package/dist/templates/next-app/src/app/api/locales/json/route.ts +13 -25
  34. package/dist/templates/next-app/src/app/api/user/login/route.ts +6 -46
  35. package/dist/templates/next-app/src/app/api/user/logout/route.ts +5 -24
  36. package/dist/templates/next-app/src/app/api/user/register/route.ts +6 -45
  37. package/dist/templates/next-app/src/app/manifest.ts +16 -0
  38. package/dist/templates/next-app/src/app/robots.txt +2 -0
  39. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +12 -2
  40. package/dist/templates/next-app/src/base/cases/RouterService.ts +5 -5
  41. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +22 -0
  42. package/dist/templates/next-app/src/base/port/IOCInterface.ts +9 -0
  43. package/dist/templates/next-app/src/base/types/{PageProps.ts → AppPageRouter.ts} +4 -1
  44. package/dist/templates/next-app/src/base/types/PagesRouter.ts +9 -0
  45. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +19 -5
  46. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +2 -2
  47. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -1
  48. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +29 -8
  49. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +4 -2
  50. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +25 -7
  51. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +1 -1
  52. package/dist/templates/next-app/src/i18n/loadMessages.ts +103 -0
  53. package/dist/templates/next-app/src/i18n/request.ts +3 -22
  54. package/dist/templates/next-app/src/pages/[locale]/about.tsx +61 -0
  55. package/dist/templates/next-app/src/pages/_app.tsx +50 -0
  56. package/dist/templates/next-app/src/pages/_document.tsx +13 -0
  57. package/dist/templates/next-app/src/{middleware.ts → proxy.ts} +2 -1
  58. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +94 -0
  59. package/dist/templates/next-app/src/server/NextApiServer.ts +53 -0
  60. package/dist/templates/next-app/src/server/PagesRouteParams.ts +136 -0
  61. package/dist/templates/next-app/src/server/{sqlBridges/SupabaseBridge.ts → SupabaseBridge.ts} +2 -0
  62. package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +74 -0
  63. package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +39 -0
  64. package/dist/templates/next-app/src/server/controllers/LocalesController.ts +33 -0
  65. package/dist/templates/next-app/src/server/controllers/UserController.ts +77 -0
  66. package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +8 -0
  67. package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +21 -0
  68. package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +11 -0
  69. package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +10 -0
  70. package/dist/templates/next-app/src/server/port/{ParamsHandlerInterface.ts → RouteParamsnHandlerInterface.ts} +9 -2
  71. package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
  72. package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +8 -0
  73. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +1 -1
  74. package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +2 -2
  75. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +8 -2
  76. package/dist/templates/next-app/src/{base → server}/services/AdminLocalesService.ts +2 -2
  77. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +25 -10
  78. package/dist/templates/next-app/src/server/services/ApiUserService.ts +5 -2
  79. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +4 -2
  80. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +1 -1
  81. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +10 -10
  82. package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -44
  83. package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -44
  84. package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -44
  85. package/dist/templates/next-app/src/styles/css/index.css +1 -1
  86. package/dist/templates/next-app/src/styles/css/scrollbar.css +34 -0
  87. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +34 -11
  88. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +69 -39
  89. package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +64 -0
  90. package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +42 -0
  91. package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +34 -0
  92. package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +17 -0
  93. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +112 -0
  94. package/dist/templates/next-app/src/uikit/{components → components-app}/LanguageSwitcher.tsx +15 -19
  95. package/dist/templates/next-app/src/uikit/{components → components-app}/ThemeSwitcher.tsx +53 -52
  96. package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +98 -0
  97. package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +93 -0
  98. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +16 -4
  99. package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +32 -0
  100. package/dist/templates/next-app/tsconfig.json +3 -2
  101. package/package.json +1 -1
  102. package/dist/templates/next-app/src/server/PageParams.ts +0 -66
  103. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +0 -80
  104. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +0 -65
  105. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +0 -58
  106. package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +0 -21
  107. /package/dist/templates/next-app/{src/app/[locale] → public}/favicon.ico +0 -0
  108. /package/dist/templates/next-app/src/uikit/{components → components-app}/LogoutButton.tsx +0 -0
@@ -1,27 +1,8 @@
1
- import { ExecutorError } from '@qlover/fe-corekit';
2
- import { NextResponse } from 'next/server';
3
- import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
4
- import { AppErrorApi } from '@/server/AppErrorApi';
5
- import { AppSuccessApi } from '@/server/AppSuccessApi';
6
- import type { UserServiceInterface } from '@/server/port/UserServiceInterface';
7
- import { UserService } from '@/server/services/UserService';
1
+ import { UserController } from '@/server/controllers/UserController';
2
+ import { NextApiServer } from '@/server/NextApiServer';
8
3
 
9
4
  export async function POST() {
10
- const server = new BootstrapServer();
11
-
12
- const result = await server.execNoError(async ({ parameters: { IOC } }) => {
13
- const userService: UserServiceInterface = IOC(UserService);
14
-
15
- await userService.logout();
16
-
17
- return true;
18
- });
19
-
20
- if (result instanceof ExecutorError) {
21
- return NextResponse.json(new AppErrorApi(result.id, result.message), {
22
- status: 400
23
- });
24
- }
25
-
26
- return NextResponse.json(new AppSuccessApi(result));
5
+ return await new NextApiServer().runWithJson(
6
+ async ({ parameters: { IOC } }) => IOC(UserController).logout()
7
+ );
27
8
  }
@@ -1,50 +1,11 @@
1
- import { ExecutorError } from '@qlover/fe-corekit';
2
- import { NextResponse } from 'next/server';
3
- import { StringEncryptor } from '@/base/cases/StringEncryptor';
4
- import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
5
- import { AppErrorApi } from '@/server/AppErrorApi';
6
- import { AppSuccessApi } from '@/server/AppSuccessApi';
7
- import type { UserServiceInterface } from '@/server/port/UserServiceInterface';
8
- import { UserService } from '@/server/services/UserService';
9
- import { LoginValidator } from '@/server/validators/LoginValidator';
1
+ import { UserController } from '@/server/controllers/UserController';
2
+ import { NextApiServer } from '@/server/NextApiServer';
10
3
  import type { NextRequest } from 'next/server';
11
4
 
12
5
  export async function POST(req: NextRequest) {
13
- const server = new BootstrapServer();
6
+ const requestBody = await req.json();
14
7
 
15
- const result = await server.execNoError(async ({ parameters: { IOC } }) => {
16
- const requestBody = await req.json();
17
-
18
- try {
19
- if (requestBody.password) {
20
- requestBody.password = IOC(StringEncryptor).decrypt(
21
- requestBody.password
22
- );
23
- }
24
- } catch {
25
- throw new ExecutorError(
26
- 'encrypt_password_failed',
27
- 'Encrypt password failed'
28
- );
29
- }
30
-
31
- const body = IOC(LoginValidator).getThrow(requestBody);
32
-
33
- const userService: UserServiceInterface = IOC(UserService);
34
-
35
- const user = await userService.register({
36
- email: body.email,
37
- password: body.password
38
- });
39
-
40
- return user;
41
- });
42
-
43
- if (result instanceof ExecutorError) {
44
- return NextResponse.json(new AppErrorApi(result.id, result.message), {
45
- status: 400
46
- });
47
- }
48
-
49
- return NextResponse.json(new AppSuccessApi(result));
8
+ return await new NextApiServer().runWithJson(
9
+ async ({ parameters: { IOC } }) => IOC(UserController).register(requestBody)
10
+ );
50
11
  }
@@ -0,0 +1,16 @@
1
+ import { getTranslations } from 'next-intl/server';
2
+ import { routing } from '@/i18n/routing';
3
+ import { COMMON_MANIFEST_NAME } from '@config/Identifier';
4
+ import type { MetadataRoute } from 'next';
5
+
6
+ export default async function manifest(): Promise<MetadataRoute.Manifest> {
7
+ const t = await getTranslations({
8
+ locale: routing.defaultLocale
9
+ });
10
+
11
+ return {
12
+ name: t(COMMON_MANIFEST_NAME),
13
+ start_url: '/',
14
+ theme_color: '#101E33'
15
+ };
16
+ }
@@ -0,0 +1,2 @@
1
+ User-Agent: *
2
+ Allow: *
@@ -1,16 +1,26 @@
1
- import { injectable } from 'inversify';
1
+ import { inject, injectable } from 'inversify';
2
+ import type { useRouter } from '@/i18n/routing';
3
+ import { I } from '@config/IOCIdentifier';
2
4
  import type { UIBridgeInterface } from '@qlover/corekit-bridge';
3
- import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
5
+ import type { LoggerInterface } from '@qlover/logger';
6
+
7
+ type AppRouterInstance = ReturnType<typeof useRouter>;
4
8
 
5
9
  @injectable()
6
10
  export class NavigateBridge implements UIBridgeInterface<AppRouterInstance> {
7
11
  protected navigate: AppRouterInstance | null = null;
8
12
 
13
+ constructor(@inject(I.Logger) protected logger: LoggerInterface) {}
14
+
9
15
  setUIBridge(ui: AppRouterInstance): void {
10
16
  this.navigate = ui;
11
17
  }
12
18
 
13
19
  getUIBridge(): AppRouterInstance | null {
20
+ if (!this.navigate) {
21
+ this.logger.debug('NavigateBridge this.navigate is not set');
22
+ }
23
+
14
24
  return this.navigate;
15
25
  }
16
26
  }
@@ -13,12 +13,8 @@ export class RouterService implements RouterInterface {
13
13
  protected uiBridge: UIBridgeInterface<AppRouterInstance>
14
14
  ) {}
15
15
 
16
- protected padLocaleHref(href: RouterPathname): string {
17
- return `/${this.locale}${href === '/' ? '' : href}`;
18
- }
19
-
20
16
  goto(href: RouterPathname): void {
21
- this.uiBridge.getUIBridge()?.push(this.padLocaleHref(href));
17
+ this.uiBridge.getUIBridge()?.push(href as string);
22
18
  }
23
19
 
24
20
  gotoHome(): void {
@@ -29,6 +25,10 @@ export class RouterService implements RouterInterface {
29
25
  this.goto('/login');
30
26
  }
31
27
 
28
+ replaceHome(): void {
29
+ this.uiBridge.getUIBridge()?.replace('/');
30
+ }
31
+
32
32
  setLocale(locale: string): void {
33
33
  this.locale = locale;
34
34
  }
@@ -12,3 +12,25 @@ export interface AppApiSuccessInterface<T = unknown> {
12
12
  export type AppApiResult<T = unknown> =
13
13
  | AppApiErrorInterface
14
14
  | AppApiSuccessInterface<T>;
15
+
16
+ export function isAppApiSuccessInterface(
17
+ result: unknown
18
+ ): result is AppApiSuccessInterface {
19
+ return (
20
+ typeof result === 'object' &&
21
+ result !== null &&
22
+ 'success' in result &&
23
+ result.success === true
24
+ );
25
+ }
26
+
27
+ export function isAppApiErrorInterface(
28
+ result: unknown
29
+ ): result is AppApiErrorInterface {
30
+ return (
31
+ typeof result === 'object' &&
32
+ result !== null &&
33
+ 'success' in result &&
34
+ result.success === false
35
+ );
36
+ }
@@ -21,4 +21,13 @@ export interface IOCInterface<
21
21
  create(
22
22
  options: IocRegisterOptions
23
23
  ): IOCFunctionInterface<IdentifierMap, IOCContainer>;
24
+
25
+ /**
26
+ * 该方法主要用于在应用启动前注册所有依赖
27
+ *
28
+ * 因为ssr渲染的原因可能需要在一些特定的环境下注册一些依赖
29
+ *
30
+ * @param options - 注册选项
31
+ */
32
+ register(options?: IocRegisterOptions): void;
24
33
  }
@@ -1,4 +1,7 @@
1
- import type { PageWithParams } from '@/server/PageParams';
1
+ /**
2
+ * 该文件主要用于 /src/app 目录下页面路由的类型定义
3
+ */
4
+ import type { PageWithParams } from '@/server/AppPageRouteParams';
2
5
 
3
6
  export interface PageParamsProps extends PageWithParams {
4
7
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 该文件主要用于 /src/pages 目录下页面路由的类型定义
3
+ */
4
+ import type { AppProps } from 'next/app';
5
+ import type { NextRouter } from 'next/router';
6
+
7
+ export type PagesRouterProps = AppProps & {
8
+ router: NextRouter;
9
+ };
@@ -1,13 +1,15 @@
1
1
  import 'reflect-metadata';
2
2
  import { Bootstrap } from '@qlover/corekit-bridge';
3
3
  import { isObject } from 'lodash';
4
+ import type { IocRegisterOptions } from '@/base/port/IOCInterface';
4
5
  import { browserGlobalsName } from '@config/common';
5
6
  import type { IOCIdentifierMap } from '@config/IOCIdentifier';
6
7
  import { BootstrapsRegistry } from './BootstrapsRegistry';
7
8
  import * as globals from '../globals';
8
9
  import type {
9
10
  IOCContainerInterface,
10
- IOCFunctionInterface
11
+ IOCFunctionInterface,
12
+ IOCRegisterInterface
11
13
  } from '@qlover/corekit-bridge';
12
14
 
13
15
  export type BootstrapAppArgs = {
@@ -23,23 +25,31 @@ export type BootstrapAppArgs = {
23
25
  * IOC容器
24
26
  */
25
27
  IOC: IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface>;
28
+
29
+ register?: IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>;
26
30
  };
27
31
 
28
32
  export class BootstrapClient {
33
+ static lastTime = 0;
29
34
  static async main(args: BootstrapAppArgs): Promise<BootstrapAppArgs> {
30
- const { root, IOC } = args;
35
+ const { logger, appConfig } = globals;
36
+
37
+ if (BootstrapClient.lastTime) {
38
+ return args;
39
+ }
40
+
41
+ const { root, IOC, register } = args;
31
42
 
32
43
  if (!isObject(root)) {
33
44
  throw new Error('root is not an object');
34
45
  }
35
46
 
36
- const { logger, appConfig } = globals;
37
-
38
47
  const bootstrap = new Bootstrap({
39
48
  root,
40
49
  logger,
41
50
  ioc: {
42
- manager: IOC
51
+ manager: IOC,
52
+ register: register
43
53
  },
44
54
  globalOptions: {
45
55
  sources: globals,
@@ -53,6 +63,10 @@ export class BootstrapClient {
53
63
  const bootstrapsRegistry = new BootstrapsRegistry(args);
54
64
 
55
65
  await bootstrap.use(bootstrapsRegistry.register()).start();
66
+
67
+ BootstrapClient.lastTime = Date.now();
68
+
69
+ logger.info('BootstrapClient starup success,', BootstrapClient.lastTime);
56
70
  } catch (error) {
57
71
  logger.error(`${appConfig.appName} starup error:`, error);
58
72
  }
@@ -4,8 +4,7 @@ import {
4
4
  type BootstrapContextValue,
5
5
  type BootstrapExecutorPlugin,
6
6
  type IOCContainerInterface,
7
- type IOCFunctionInterface,
8
- type LoggerInterface
7
+ type IOCFunctionInterface
9
8
  } from '@qlover/corekit-bridge';
10
9
  import {
11
10
  AsyncExecutor,
@@ -16,6 +15,7 @@ import {
16
15
  import type { ServerInterface } from '@/server/port/ServerInterface';
17
16
  import { I, type IOCIdentifierMapServer } from '@config/IOCIdentifier';
18
17
  import { ServerIOC } from '../serverIoc/ServerIOC';
18
+ import type { LoggerInterface } from '@qlover/logger';
19
19
 
20
20
  export interface BootstrapServerContextValue extends BootstrapContextValue {
21
21
  IOC: IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>;
@@ -41,7 +41,6 @@ export class BootstrapsRegistry {
41
41
  }
42
42
 
43
43
  bootstrapList.push(IocIdentifierTest);
44
- // TODO: 需要使用到
45
44
 
46
45
  return bootstrapList;
47
46
  }
@@ -1,13 +1,17 @@
1
1
  import {
2
2
  createIOCFunction,
3
3
  type IOCContainerInterface,
4
- type IOCFunctionInterface
4
+ type IOCFunctionInterface,
5
+ type IOCRegisterInterface
5
6
  } from '@qlover/corekit-bridge';
6
7
  import { InversifyContainer } from '@/base/cases/InversifyContainer';
7
- import type { IOCInterface } from '@/base/port/IOCInterface';
8
+ import type {
9
+ IOCInterface,
10
+ IocRegisterOptions
11
+ } from '@/base/port/IOCInterface';
8
12
  import type { IOCIdentifierMap } from '@config/IOCIdentifier';
9
13
  import { ClientIOCRegister } from './ClientIOCRegister';
10
- import { appConfig } from '../globals';
14
+ import { logger } from '../globals';
11
15
 
12
16
  export class ClientIOC implements IOCInterface<
13
17
  IOCIdentifierMap,
@@ -17,6 +21,14 @@ export class ClientIOC implements IOCInterface<
17
21
  IOCIdentifierMap,
18
22
  IOCContainerInterface
19
23
  > | null = null;
24
+ protected registers = 0;
25
+
26
+ constructor(
27
+ protected readonly iocRegister?: IOCRegisterInterface<
28
+ IOCContainerInterface,
29
+ IocRegisterOptions
30
+ >
31
+ ) {}
20
32
 
21
33
  create(): IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface> {
22
34
  if (this.ioc) {
@@ -25,13 +37,22 @@ export class ClientIOC implements IOCInterface<
25
37
 
26
38
  this.ioc = createIOCFunction<IOCIdentifierMap>(new InversifyContainer());
27
39
 
28
- const register = new ClientIOCRegister({
29
- appConfig: appConfig
30
- });
40
+ return this.ioc;
41
+ }
42
+
43
+ register(options: IocRegisterOptions): void {
44
+ if (this.registers > 0) {
45
+ return;
46
+ }
47
+
48
+ if (!this.ioc) {
49
+ return;
50
+ }
31
51
 
32
- register.register(this.ioc.implemention!, this.ioc);
52
+ logger.info('ClientIOC register');
53
+ new ClientIOCRegister(options).register(this.ioc.implemention!, this.ioc);
33
54
 
34
- return this.ioc;
55
+ this.registers++;
35
56
  }
36
57
  }
37
58
 
@@ -1,7 +1,9 @@
1
+ import { CookieStorage } from '@qlover/corekit-bridge';
1
2
  import { RouterService } from '@/base/cases/RouterService';
2
3
  import type { IocRegisterOptions } from '@/base/port/IOCInterface';
3
4
  import { I18nService } from '@/base/services/I18nService';
4
5
  import { UserService } from '@/base/services/UserService';
6
+ import { cookiesConfig } from '@config/cookies';
5
7
  import { IOCIdentifier as I } from '@config/IOCIdentifier';
6
8
  import { dialogHandler, logger, JSON } from '../globals';
7
9
  import type {
@@ -34,7 +36,7 @@ export class ClientIOCRegister implements IOCRegisterInterface<
34
36
  // ioc.bind(I.AntdStaticApiInterface, dialogHandler);
35
37
  // ioc.bind(I.LocalStorage, globals.localStorage);
36
38
  // ioc.bind(I.LocalStorageEncrypt, localStorageEncrypt);
37
- // ioc.bind(I.CookieStorage, globals.cookieStorage);
39
+ ioc.bind(I.CookieStorage, new CookieStorage(cookiesConfig));
38
40
  }
39
41
 
40
42
  /**
@@ -87,7 +89,7 @@ export class ClientIOCRegister implements IOCRegisterInterface<
87
89
  /**
88
90
  * @override
89
91
  */
90
- register(
92
+ public register(
91
93
  ioc: IOCContainerInterface,
92
94
  _manager: IOCManagerInterface<IOCContainerInterface>
93
95
  ): void {
@@ -5,7 +5,10 @@ import {
5
5
  } from '@qlover/corekit-bridge';
6
6
  import { AppConfig } from '@/base/cases/AppConfig';
7
7
  import { InversifyContainer } from '@/base/cases/InversifyContainer';
8
- import type { IOCInterface } from '@/base/port/IOCInterface';
8
+ import type {
9
+ IOCInterface,
10
+ IocRegisterOptions
11
+ } from '@/base/port/IOCInterface';
9
12
  import type { IOCIdentifierMapServer } from '@config/IOCIdentifier';
10
13
  import { ServerIOCRegister } from './ServerIOCRegister';
11
14
 
@@ -15,6 +18,8 @@ export class ServerIOC implements IOCInterface<
15
18
  > {
16
19
  static instance: ServerIOC | null = null;
17
20
 
21
+ protected registers = 0;
22
+
18
23
  protected ioc: IOCFunctionInterface<
19
24
  IOCIdentifierMapServer,
20
25
  IOCContainerInterface
@@ -38,16 +43,29 @@ export class ServerIOC implements IOCInterface<
38
43
  return this.ioc;
39
44
  }
40
45
 
41
- this.ioc = createIOCFunction<IOCIdentifierMapServer>(
42
- new InversifyContainer()
43
- );
46
+ this.ioc = createIOCFunction(new InversifyContainer());
44
47
 
45
- const register = new ServerIOCRegister({
48
+ // 注册默认依赖
49
+ this.register({
46
50
  appConfig: new AppConfig()
47
51
  });
48
52
 
49
- register.register(this.ioc.implemention!, this.ioc);
50
-
51
53
  return this.ioc;
52
54
  }
55
+
56
+ register(options: IocRegisterOptions): void {
57
+ if (this.registers > 0) {
58
+ console.debug('ServerIOC: ioc already registered');
59
+ return;
60
+ }
61
+
62
+ if (!this.ioc) {
63
+ console.debug('ServerIOC: ioc not initialized');
64
+ return;
65
+ }
66
+
67
+ new ServerIOCRegister(options).register(this.ioc.implemention!, this.ioc);
68
+
69
+ this.registers++;
70
+ }
53
71
  }
@@ -5,7 +5,7 @@ import {
5
5
  } from '@qlover/corekit-bridge';
6
6
  import { Logger, ConsoleHandler, TimestampFormatter } from '@qlover/logger';
7
7
  import type { IocRegisterOptions } from '@/base/port/IOCInterface';
8
- import { SupabaseBridge } from '@/server/sqlBridges/SupabaseBridge';
8
+ import { SupabaseBridge } from '@/server/SupabaseBridge';
9
9
  import { IOCIdentifier as I } from '@config/IOCIdentifier';
10
10
 
11
11
  export class ServerIOCRegister implements IOCRegisterInterface<
@@ -0,0 +1,103 @@
1
+ import { useApiLocales } from '@config/common';
2
+
3
+ /**
4
+ * 加载 i18n 消息的公共方法
5
+ * 支持从 API 加载或从 JSON 文件加载
6
+ *
7
+ * @param locale - 要加载的语言代码
8
+ * @param namespace - 可选的命名空间(单个字符串或字符串数组),如果提供则只返回该命名空间下的消息,保留命名空间前缀
9
+ * @returns Promise<Record<string, string>> 返回翻译消息对象
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // 加载所有消息
14
+ * const allMessages = await loadMessages('en');
15
+ *
16
+ * // 加载单个命名空间
17
+ * const commonMessages = await loadMessages('en', 'common');
18
+ *
19
+ * // 加载多个命名空间
20
+ * const messages = await loadMessages('en', ['common', 'page_home']);
21
+ * ```
22
+ */
23
+ export async function loadMessages(
24
+ locale: string,
25
+ namespace?: string | string[]
26
+ ): Promise<Record<string, string>> {
27
+ let allMessages: Record<string, string>;
28
+
29
+ // 如果配置了使用 API 加载本地化数据
30
+ if (useApiLocales) {
31
+ try {
32
+ const app_host = process.env.APP_HOST;
33
+ const localeUrl = `${app_host}/api/locales/json?locale=${locale}`;
34
+ const response = await fetch(localeUrl);
35
+
36
+ if (!response.ok) {
37
+ throw new Error(
38
+ `Failed to fetch locale from API: ${response.statusText}`
39
+ );
40
+ }
41
+
42
+ allMessages = await response.json();
43
+ } catch (error) {
44
+ console.warn(`Failed to load locale from API for ${locale}`, error);
45
+ // 如果 API 加载失败,继续尝试从文件加载
46
+ allMessages = (await import(`../../public/locales/${locale}.json`))
47
+ .default;
48
+ }
49
+ } else {
50
+ allMessages = (await import(`../../public/locales/${locale}.json`)).default;
51
+ }
52
+
53
+ // 如果指定了命名空间,进行过滤
54
+ return filterMessagesByNamespace(allMessages, namespace);
55
+ }
56
+
57
+ /**
58
+ * 根据命名空间过滤消息,保留命名空间前缀
59
+ *
60
+ * @param messages - 所有消息对象
61
+ * @param namespace - 可选的命名空间(单个字符串或字符串数组),如果提供则只返回该命名空间下的消息,保留命名空间前缀
62
+ * @returns 过滤后的消息对象
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const allMessages = { "common:save": "Save", "common:cancel": "Cancel", "page_home:title": "Home" };
67
+ *
68
+ * // 单个命名空间
69
+ * const commonMessages = filterMessagesByNamespace(allMessages, "common");
70
+ * // 返回: { "common:save": "Save", "common:cancel": "Cancel" }
71
+ *
72
+ * // 多个命名空间
73
+ * const messages = filterMessagesByNamespace(allMessages, ["common", "page_home"]);
74
+ * // 返回: { "common:save": "Save", "common:cancel": "Cancel", "page_home:title": "Home" }
75
+ * ```
76
+ */
77
+ export function filterMessagesByNamespace(
78
+ messages: Record<string, string>,
79
+ namespace?: string | string[]
80
+ ): Record<string, string> {
81
+ // 如果没有指定命名空间,返回所有消息
82
+ if (!namespace) {
83
+ return messages;
84
+ }
85
+
86
+ // 将单个字符串转换为数组,统一处理
87
+ const namespaces = Array.isArray(namespace) ? namespace : [namespace];
88
+ const filteredMessages: Record<string, string> = {};
89
+
90
+ // 遍历所有命名空间
91
+ for (const ns of namespaces) {
92
+ const namespacePrefix = `${ns}:`;
93
+
94
+ for (const [key, value] of Object.entries(messages)) {
95
+ if (key.startsWith(namespacePrefix)) {
96
+ // 保留完整的键名(包括命名空间前缀)
97
+ filteredMessages[key] = value;
98
+ }
99
+ }
100
+ }
101
+
102
+ return filteredMessages;
103
+ }
@@ -1,7 +1,7 @@
1
1
  // src/i18n/request.ts
2
2
 
3
3
  import { getRequestConfig } from 'next-intl/server';
4
- import { useApiLocales } from '@config/common';
4
+ import { loadMessages } from './loadMessages';
5
5
  import { routing, type Locale } from './routing';
6
6
 
7
7
  // Export a function to configure next-intl on each request (server-side)
@@ -14,30 +14,11 @@ export default getRequestConfig(async ({ requestLocale }) => {
14
14
  locale = routing.defaultLocale;
15
15
  }
16
16
 
17
- // use api locales
18
- if (useApiLocales) {
19
- const app_host = process.env.APP_HOST;
20
- const localeUrl = `${app_host}/api/locales/json?locale=${locale}`;
21
- const response = await fetch(localeUrl);
22
- const data = await response.json();
23
- return {
24
- locale,
25
- messages: data,
26
- // 将 MISSING_MESSAGE 错误转换为警告
27
- onError: (error) => {
28
- if (error.message.includes('MISSING_MESSAGE')) {
29
- console.warn(`[i18n] Missing translation: ${error.message}`);
30
- return error.message; // 返回 key 作为 fallback 文本
31
- }
32
- throw error; // 其他错误仍然抛出
33
- }
34
- };
35
- }
17
+ const messages = await loadMessages(locale);
36
18
 
37
- // Dynamically import the translation messages for the selected locale
38
19
  return {
39
20
  locale,
40
- messages: (await import(`../../public/locales/${locale}.json`)).default,
21
+ messages,
41
22
  // 将 MISSING_MESSAGE 错误转换为警告
42
23
  onError: (error) => {
43
24
  if (error.message.includes('MISSING_MESSAGE')) {