@qlover/create-app 0.7.7 → 0.7.8

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 (46) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/build/generateLocales.ts +1 -1
  5. package/dist/templates/next-app/config/IOCIdentifier.ts +15 -2
  6. package/dist/templates/next-app/config/Identifier/common.error.ts +7 -0
  7. package/dist/templates/next-app/package.json +1 -1
  8. package/dist/templates/next-app/public/locales/{en/common.json → en.json} +2 -1
  9. package/dist/templates/next-app/public/locales/{zh/common.json → zh.json} +2 -1
  10. package/dist/templates/next-app/src/app/[locale]/layout.tsx +8 -20
  11. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +6 -7
  12. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +24 -11
  13. package/dist/templates/next-app/src/app/[locale]/page.tsx +14 -1
  14. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +92 -0
  15. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +16 -0
  16. package/dist/templates/next-app/src/base/cases/PageParams.ts +74 -0
  17. package/dist/templates/next-app/src/base/cases/RouterService.ts +35 -0
  18. package/dist/templates/next-app/src/base/cases/ServerAuth.ts +17 -0
  19. package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +27 -0
  20. package/dist/templates/next-app/src/base/port/IOCInterface.ts +24 -0
  21. package/dist/templates/next-app/src/base/port/ParamsHandlerInterface.ts +11 -0
  22. package/dist/templates/next-app/src/base/port/RouterInterface.ts +11 -0
  23. package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +3 -0
  24. package/dist/templates/next-app/src/base/port/ServerInterface.ts +12 -0
  25. package/dist/templates/next-app/src/base/types/PageProps.ts +9 -0
  26. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +2 -39
  27. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +78 -0
  28. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +37 -0
  29. package/dist/templates/next-app/src/core/{IocRegisterImpl.ts → clientIoc/ClientIOCRegister.ts} +20 -23
  30. package/dist/templates/next-app/src/core/globals.ts +3 -0
  31. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +52 -0
  32. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +63 -0
  33. package/dist/templates/next-app/src/i18n/request.ts +1 -2
  34. package/dist/templates/next-app/src/i18n/routing.ts +3 -3
  35. package/dist/templates/next-app/src/middleware.ts +6 -3
  36. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +9 -4
  37. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +13 -1
  38. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +5 -0
  39. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +34 -0
  40. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +16 -3
  41. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +9 -2
  42. package/package.json +1 -1
  43. package/dist/templates/next-app/plugins/eslint-plugin-testid.mjs +0 -94
  44. package/dist/templates/next-app/plugins/generateLocalesPlugin.ts +0 -33
  45. package/dist/templates/next-app/src/core/IOC.ts +0 -58
  46. package/dist/templates/next-app/src/server/getServerI18n.ts +0 -26
@@ -0,0 +1,35 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import { NavigateBridge } from './NavigateBridge';
3
+ import type { RouterInterface, RouterPathname } from '../port/RouterInterface';
4
+ import type { UIBridgeInterface } from '@qlover/corekit-bridge';
5
+ import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
6
+
7
+ @injectable()
8
+ export class RouterService implements RouterInterface {
9
+ protected locale: string = '';
10
+
11
+ constructor(
12
+ @inject(NavigateBridge)
13
+ protected uiBridge: UIBridgeInterface<AppRouterInstance>
14
+ ) {}
15
+
16
+ protected padLocaleHref(href: RouterPathname): string {
17
+ return `/${this.locale}${href === '/' ? '' : href}`;
18
+ }
19
+
20
+ goto(href: RouterPathname): void {
21
+ this.uiBridge.getUIBridge()?.push(this.padLocaleHref(href));
22
+ }
23
+
24
+ gotoHome(): void {
25
+ this.goto('/');
26
+ }
27
+
28
+ setLocale(locale: string): void {
29
+ this.locale = locale;
30
+ }
31
+
32
+ getLocale(): string {
33
+ return this.locale;
34
+ }
35
+ }
@@ -0,0 +1,17 @@
1
+ import { cookies } from 'next/headers';
2
+ import { I } from '@config/IOCIdentifier';
3
+ import type { ServerAuthInterface } from '../port/ServerAuthInterface';
4
+ import type { ServerInterface } from '../port/ServerInterface';
5
+
6
+ export class ServerAuth implements ServerAuthInterface {
7
+ constructor(protected server: ServerInterface) {}
8
+
9
+ async hasAuth(): Promise<boolean> {
10
+ const cookieStore = await cookies();
11
+ const appConfig = this.server.getIOC(I.AppConfig);
12
+
13
+ const token = cookieStore.get(appConfig.userTokenKey);
14
+
15
+ return !!token;
16
+ }
17
+ }
@@ -0,0 +1,27 @@
1
+ import { ExecutorError, type ExecutorContext } from '@qlover/fe-corekit';
2
+ import { SERVER_AUTH_ERROR } from '@config/Identifier';
3
+ import type { BootstrapServerContextValue } from '@/core/bootstraps/BootstrapServer';
4
+ import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
5
+
6
+ export class ServerErrorHandler implements BootstrapExecutorPlugin {
7
+ pluginName = 'ServerErrorHandler';
8
+
9
+ onError(
10
+ context: ExecutorContext<BootstrapServerContextValue>
11
+ ): ExecutorError | void {
12
+ const { parameters, error } = context;
13
+ const { messages } = parameters;
14
+
15
+ if (error instanceof Error) {
16
+ const messageId = error.message;
17
+
18
+ if (messageId === SERVER_AUTH_ERROR) {
19
+ const message = messages[messageId];
20
+
21
+ if (message) {
22
+ return new ExecutorError(messageId, message);
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,24 @@
1
+ import type {
2
+ EnvConfigInterface,
3
+ IOCContainerInterface,
4
+ IOCFunctionInterface
5
+ } from '@qlover/corekit-bridge';
6
+
7
+ /**
8
+ * IOC register options
9
+ */
10
+ export type IocRegisterOptions = {
11
+ /**
12
+ * The app config
13
+ */
14
+ appConfig: EnvConfigInterface;
15
+ };
16
+
17
+ export interface IOCInterface<
18
+ IdentifierMap,
19
+ IOCContainer extends IOCContainerInterface
20
+ > {
21
+ create(
22
+ options: IocRegisterOptions
23
+ ): IOCFunctionInterface<IdentifierMap, IOCContainer>;
24
+ }
@@ -0,0 +1,11 @@
1
+ import type { PageI18nInterface } from '@config/i18n';
2
+
3
+ export interface ParamsHandlerInterface {
4
+ getLocale(defaultLocale?: string): string;
5
+ getI18nWithNotFound(): string;
6
+ getI18nMessages(): Promise<Record<string, string>>;
7
+ getI18nInterface<T extends PageI18nInterface>(
8
+ i18nInterface: T,
9
+ namespace?: string
10
+ ): Promise<T>;
11
+ }
@@ -0,0 +1,11 @@
1
+ import type { redirect } from '@/i18n/routing';
2
+ export type RouterPathname = Parameters<typeof redirect>[0]['href'];
3
+
4
+ export interface RouterInterface {
5
+ gotoHome(): void;
6
+ goto(href: RouterPathname): void;
7
+
8
+ getLocale(): string;
9
+
10
+ setLocale(locale: string): void;
11
+ }
@@ -0,0 +1,3 @@
1
+ export interface ServerAuthInterface {
2
+ hasAuth(): Promise<boolean>;
3
+ }
@@ -0,0 +1,12 @@
1
+ import {
2
+ type IOCContainerInterface,
3
+ type IOCFunctionInterface
4
+ } from '@qlover/corekit-bridge';
5
+ import { type IOCIdentifierMapServer } from '@config/IOCIdentifier';
6
+
7
+ export interface ServerInterface {
8
+ getIOC(): IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>;
9
+ getIOC<T extends keyof IOCIdentifierMapServer>(
10
+ identifier: T
11
+ ): IOCIdentifierMapServer[T];
12
+ }
@@ -0,0 +1,9 @@
1
+ import type { PageWithParams } from '@/base/cases/PageParams';
2
+
3
+ export interface PageParamsProps extends PageWithParams {
4
+ searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
5
+ }
6
+
7
+ export interface PageLayoutProps extends PageWithParams {
8
+ children: React.ReactNode;
9
+ }
@@ -1,18 +1,12 @@
1
- import { Bootstrap, createIOCFunction } from '@qlover/corekit-bridge';
1
+ import { Bootstrap } from '@qlover/corekit-bridge';
2
2
  import { isObject } from 'lodash';
3
3
  import { browserGlobalsName } from '@config/common';
4
- import { InversifyContainer } from '@/base/cases/InversifyContainer';
5
4
  import { BootstrapsRegistry } from './BootstrapsRegistry';
6
5
  import * as globals from '../globals';
7
- import { appConfig } from '../globals';
8
- import { IocRegisterImpl } from '../IocRegisterImpl';
9
- import type { IOCContainer } from '../IOC';
10
6
  import type { IOCIdentifierMap } from '@config/IOCIdentifier';
11
7
  import type {
12
- EnvConfigInterface,
13
8
  IOCContainerInterface,
14
- IOCFunctionInterface,
15
- IOCManagerInterface
9
+ IOCFunctionInterface
16
10
  } from '@qlover/corekit-bridge';
17
11
 
18
12
  export type BootstrapAppArgs = {
@@ -31,37 +25,6 @@ export type BootstrapAppArgs = {
31
25
  };
32
26
 
33
27
  export class BootstrapClient {
34
- private static _ioc: IOCFunctionInterface<
35
- IOCIdentifierMap,
36
- IOCContainerInterface
37
- > | null = null;
38
-
39
- static createSingletonIOC(): IOCFunctionInterface<
40
- IOCIdentifierMap,
41
- IOCContainerInterface
42
- > {
43
- if (BootstrapClient._ioc) {
44
- return BootstrapClient._ioc;
45
- }
46
-
47
- BootstrapClient._ioc = createIOCFunction<IOCIdentifierMap>(
48
- /**
49
- * If not inversify, you can use any IOC container,
50
- * then replace the InversifyContainer with your own IOC container
51
- */
52
- new InversifyContainer()
53
- );
54
-
55
- new IocRegisterImpl({
56
- appConfig: appConfig as EnvConfigInterface
57
- }).register(
58
- BootstrapClient._ioc.implemention as IOCContainer,
59
- BootstrapClient._ioc as IOCManagerInterface<IOCContainer>
60
- );
61
-
62
- return BootstrapClient._ioc;
63
- }
64
-
65
28
  static async main(args: BootstrapAppArgs): Promise<BootstrapAppArgs> {
66
29
  const { root, IOC } = args;
67
30
 
@@ -0,0 +1,78 @@
1
+ import {
2
+ type BootstrapContextValue,
3
+ type BootstrapExecutorPlugin,
4
+ type IOCContainerInterface,
5
+ type IOCFunctionInterface,
6
+ type LoggerInterface
7
+ } from '@qlover/corekit-bridge';
8
+ import { AsyncExecutor, type ExecutorPlugin } from '@qlover/fe-corekit';
9
+ import { I, type IOCIdentifierMapServer } from '@config/IOCIdentifier';
10
+ import type { ServerInterface } from '@/base/port/ServerInterface';
11
+ import { ServerIOC } from '../serverIoc/ServerIOC';
12
+
13
+ export interface BootstrapServerResult {
14
+ locale: string;
15
+ messages: Record<string, string>;
16
+ }
17
+
18
+ export interface BootstrapServerContextValue extends BootstrapContextValue {
19
+ locale: string;
20
+ messages: Record<string, string>;
21
+ }
22
+
23
+ export class BootstrapServer implements ServerInterface {
24
+ protected executor: AsyncExecutor;
25
+ protected root: Record<string, unknown> = {};
26
+ protected IOC: IOCFunctionInterface<
27
+ IOCIdentifierMapServer,
28
+ IOCContainerInterface
29
+ >;
30
+ readonly logger: LoggerInterface;
31
+
32
+ constructor() {
33
+ const serverIOC = ServerIOC.create();
34
+ const ioc = serverIOC.create();
35
+ const logger = ioc(I.Logger);
36
+
37
+ this.executor = new AsyncExecutor();
38
+ this.IOC = ioc;
39
+ this.logger = logger;
40
+ }
41
+
42
+ getIOC(): IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>;
43
+ getIOC<T extends keyof IOCIdentifierMapServer>(
44
+ identifier: T
45
+ ): IOCIdentifierMapServer[T];
46
+ getIOC(
47
+ identifier?: keyof IOCIdentifierMapServer
48
+ ):
49
+ | IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>
50
+ | IOCIdentifierMapServer[keyof IOCIdentifierMapServer] {
51
+ if (identifier === undefined) {
52
+ return this.IOC;
53
+ }
54
+ return this.IOC(identifier);
55
+ }
56
+
57
+ /**
58
+ * @override
59
+ */
60
+ use(
61
+ plugin:
62
+ | BootstrapExecutorPlugin
63
+ | BootstrapExecutorPlugin[]
64
+ | ((
65
+ ioc: IOCFunctionInterface<
66
+ IOCIdentifierMapServer,
67
+ IOCContainerInterface
68
+ >
69
+ ) => BootstrapExecutorPlugin)
70
+ ): this {
71
+ if (typeof plugin === 'function') {
72
+ plugin = plugin(this.IOC);
73
+ }
74
+
75
+ this.executor.use(plugin as ExecutorPlugin<unknown>);
76
+ return this;
77
+ }
78
+ }
@@ -0,0 +1,37 @@
1
+ import {
2
+ createIOCFunction,
3
+ type IOCContainerInterface,
4
+ type IOCFunctionInterface
5
+ } from '@qlover/corekit-bridge';
6
+ import { InversifyContainer } from '@/base/cases/InversifyContainer';
7
+ import type { IOCInterface } from '@/base/port/IOCInterface';
8
+ import { ClientIOCRegister } from './ClientIOCRegister';
9
+ import { appConfig } from '../globals';
10
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
11
+
12
+ export class ClientIOC
13
+ implements IOCInterface<IOCIdentifierMap, IOCContainerInterface>
14
+ {
15
+ protected ioc: IOCFunctionInterface<
16
+ IOCIdentifierMap,
17
+ IOCContainerInterface
18
+ > | null = null;
19
+
20
+ create(): IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface> {
21
+ if (this.ioc) {
22
+ return this.ioc;
23
+ }
24
+
25
+ this.ioc = createIOCFunction<IOCIdentifierMap>(new InversifyContainer());
26
+
27
+ const register = new ClientIOCRegister({
28
+ appConfig: appConfig
29
+ });
30
+
31
+ register.register(this.ioc.implemention!, this.ioc);
32
+
33
+ return this.ioc;
34
+ }
35
+ }
36
+
37
+ export const clientIOC = new ClientIOC();
@@ -1,10 +1,18 @@
1
1
  import { IOCIdentifier as I } from '@config/IOCIdentifier';
2
+ import { RouterService } from '@/base/cases/RouterService';
3
+ import type { IocRegisterOptions } from '@/base/port/IOCInterface';
2
4
  import { I18nService } from '@/base/services/I18nService';
3
- import { logger, JSON } from './globals';
4
- import type { IOCContainer, IOCRegister, IocRegisterOptions } from './IOC';
5
- import type { IOCManagerInterface } from '@qlover/corekit-bridge';
5
+ import { UserService } from '@/base/services/UserService';
6
+ import { dialogHandler, logger, JSON } from '../globals';
7
+ import type {
8
+ IOCContainerInterface,
9
+ IOCManagerInterface,
10
+ IOCRegisterInterface
11
+ } from '@qlover/corekit-bridge';
6
12
 
7
- export class IocRegisterImpl implements IOCRegister {
13
+ export class ClientIOCRegister
14
+ implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
15
+ {
8
16
  constructor(protected options: IocRegisterOptions) {}
9
17
 
10
18
  /**
@@ -14,13 +22,13 @@ export class IocRegisterImpl implements IOCRegister {
14
22
  *
15
23
  * @param ioc - IOC container
16
24
  */
17
- protected registerGlobals(ioc: IOCContainer): void {
25
+ protected registerGlobals(ioc: IOCContainerInterface): void {
18
26
  const { appConfig } = this.options;
19
27
  ioc.bind(I.JSONSerializer, JSON);
20
28
  ioc.bind(I.Logger, logger);
21
29
  ioc.bind(I.AppConfig, appConfig);
22
30
  // ioc.bind(I.EnvConfigInterface, appConfig);
23
- // ioc.bind(I.DialogHandler, dialogHandler);
31
+ ioc.bind(I.DialogHandler, dialogHandler);
24
32
  // ioc.bind(I.UIDialogInterface, dialogHandler);
25
33
  // ioc.bind(I.AntdStaticApiInterface, dialogHandler);
26
34
  // ioc.bind(I.LocalStorage, globals.localStorage);
@@ -38,20 +46,9 @@ export class IocRegisterImpl implements IOCRegister {
38
46
  *
39
47
  * @param ioc
40
48
  */
41
- protected registerImplement(ioc: IOCContainer): void {
49
+ protected registerImplement(ioc: IOCContainerInterface): void {
42
50
  ioc.bind(I.I18nServiceInterface, new I18nService());
43
- // ioc.bind(
44
- // I.RouteServiceInterface,
45
- // new RouteService(
46
- // ioc.get(NavigateBridge),
47
- // ioc.get(I.I18nServiceInterface),
48
- // {
49
- // routes: useLocaleRoutes ? baseRoutes : baseNoLocaleRoutes,
50
- // logger: ioc.get(I.Logger),
51
- // hasLocalRoutes: useLocaleRoutes
52
- // }
53
- // )
54
- // );
51
+ ioc.bind(I.RouterServiceInterface, ioc.get(RouterService));
55
52
  // ioc.bind(
56
53
  // I.ThemeService,
57
54
  // new ThemeService({
@@ -61,14 +58,14 @@ export class IocRegisterImpl implements IOCRegister {
61
58
  // );
62
59
  // ioc.bind(I.I18nKeyErrorPlugin, ioc.get(I18nKeyErrorPlugin));
63
60
  // ioc.bind(I.ProcesserExecutorInterface, ioc.get(ProcesserExecutor));
64
- // ioc.bind(I.UserServiceInterface, ioc.get(UserService));
61
+ ioc.bind(I.UserServiceInterface, ioc.get(UserService));
65
62
  // ioc.bind(I.RequestCatcherInterface, ioc.get(RequestStatusCatcher));
66
63
  // ioc.bind(I.ExecutorPageBridgeInterface, ioc.get(ExecutorPageBridge));
67
64
  // ioc.bind(I.JSONStoragePageInterface, ioc.get(JSONStoragePageBridge));
68
65
  // ioc.bind(I.RequestPageBridgeInterface, ioc.get(RequestPageBridge));
69
66
  }
70
67
 
71
- protected registerCommon(_ioc: IOCContainer): void {
68
+ protected registerCommon(_ioc: IOCContainerInterface): void {
72
69
  // const { appConfig } = this.options;
73
70
  // const logger = ioc.get(I.Logger);
74
71
  // const feApiRequestCommonPlugin = new RequestCommonPlugin({
@@ -90,8 +87,8 @@ export class IocRegisterImpl implements IOCRegister {
90
87
  * @override
91
88
  */
92
89
  register(
93
- ioc: IOCContainer,
94
- _manager: IOCManagerInterface<IOCContainer>
90
+ ioc: IOCContainerInterface,
91
+ _manager: IOCManagerInterface<IOCContainerInterface>
95
92
  ): void {
96
93
  this.registerGlobals(ioc);
97
94
  this.registerCommon(ioc);
@@ -3,9 +3,12 @@ import { ColorFormatter, ConsoleHandler, Logger } from '@qlover/corekit-bridge';
3
3
  import { JSONSerializer } from '@qlover/fe-corekit';
4
4
  import { loggerStyles } from '@config/common';
5
5
  import { AppConfig } from '@/base/cases/AppConfig';
6
+ import { DialogHandler } from '@/base/cases/DialogHandler';
6
7
 
7
8
  export const appConfig = new AppConfig();
8
9
 
10
+ export const dialogHandler = new DialogHandler();
11
+
9
12
  /**
10
13
  * Global logger
11
14
  */
@@ -0,0 +1,52 @@
1
+ import {
2
+ createIOCFunction,
3
+ type IOCContainerInterface,
4
+ type IOCFunctionInterface
5
+ } from '@qlover/corekit-bridge';
6
+ import { AppConfig } from '@/base/cases/AppConfig';
7
+ import { InversifyContainer } from '@/base/cases/InversifyContainer';
8
+ import type { IOCInterface } from '@/base/port/IOCInterface';
9
+ import { ServerIOCRegister } from './ServerIOCRegister';
10
+ import type { IOCIdentifierMapServer } from '@config/IOCIdentifier';
11
+
12
+ export class ServerIOC
13
+ implements IOCInterface<IOCIdentifierMapServer, IOCContainerInterface>
14
+ {
15
+ static instance: ServerIOC | null = null;
16
+
17
+ protected ioc: IOCFunctionInterface<
18
+ IOCIdentifierMapServer,
19
+ IOCContainerInterface
20
+ > | null = null;
21
+
22
+ static create(): ServerIOC {
23
+ if (this.instance) {
24
+ return this.instance;
25
+ }
26
+
27
+ this.instance = new ServerIOC();
28
+
29
+ return this.instance;
30
+ }
31
+
32
+ create(): IOCFunctionInterface<
33
+ IOCIdentifierMapServer,
34
+ IOCContainerInterface
35
+ > {
36
+ if (this.ioc) {
37
+ return this.ioc;
38
+ }
39
+
40
+ this.ioc = createIOCFunction<IOCIdentifierMapServer>(
41
+ new InversifyContainer()
42
+ );
43
+
44
+ const register = new ServerIOCRegister({
45
+ appConfig: new AppConfig()
46
+ });
47
+
48
+ register.register(this.ioc.implemention!, this.ioc);
49
+
50
+ return this.ioc;
51
+ }
52
+ }
@@ -0,0 +1,63 @@
1
+ import {
2
+ ConsoleHandler,
3
+ Logger,
4
+ TimestampFormatter,
5
+ type IOCContainerInterface,
6
+ type IOCManagerInterface,
7
+ type IOCRegisterInterface
8
+ } from '@qlover/corekit-bridge';
9
+ import { IOCIdentifier as I } from '@config/IOCIdentifier';
10
+ import type { IocRegisterOptions } from '@/base/port/IOCInterface';
11
+
12
+ export class ServerIOCRegister
13
+ implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
14
+ {
15
+ constructor(protected options: IocRegisterOptions) {}
16
+
17
+ /**
18
+ * Register globals
19
+ *
20
+ * 一般用于注册全局
21
+ *
22
+ * @param ioc - IOC container
23
+ */
24
+ protected registerGlobals(ioc: IOCContainerInterface): void {
25
+ const { appConfig } = this.options;
26
+ ioc.bind(I.AppConfig, appConfig);
27
+ ioc.bind(
28
+ I.Logger,
29
+ new Logger({
30
+ handlers: new ConsoleHandler(
31
+ new TimestampFormatter({
32
+ localeOptions: {
33
+ year: '2-digit',
34
+ month: '2-digit',
35
+ day: '2-digit',
36
+ hour: '2-digit',
37
+ minute: '2-digit',
38
+ second: '2-digit'
39
+ }
40
+ })
41
+ ),
42
+ silent: false,
43
+ level: appConfig.env === 'development' ? 'debug' : 'info'
44
+ })
45
+ );
46
+ }
47
+
48
+ protected registerImplement(_ioc: IOCContainerInterface): void {}
49
+
50
+ protected registerCommon(_ioc: IOCContainerInterface): void {}
51
+
52
+ /**
53
+ * @override
54
+ */
55
+ register(
56
+ ioc: IOCContainerInterface,
57
+ _manager: IOCManagerInterface<IOCContainerInterface>
58
+ ): void {
59
+ this.registerGlobals(ioc);
60
+ this.registerCommon(ioc);
61
+ this.registerImplement(ioc);
62
+ }
63
+ }
@@ -16,7 +16,6 @@ export default getRequestConfig(async ({ requestLocale }) => {
16
16
  // Dynamically import the translation messages for the selected locale
17
17
  return {
18
18
  locale,
19
- messages: (await import(`../../public/locales/${locale}/common.json`))
20
- .default
19
+ messages: (await import(`../../public/locales/${locale}.json`)).default
21
20
  };
22
21
  });
@@ -19,9 +19,9 @@ export const routing = defineRouting({
19
19
  en: '/',
20
20
  zh: '/'
21
21
  },
22
- '/about': {
23
- en: '/about',
24
- zh: '/about'
22
+ '/login': {
23
+ en: '/login',
24
+ zh: '/login'
25
25
  }
26
26
  }
27
27
  });
@@ -15,8 +15,11 @@ export const config = {
15
15
  matcher: [
16
16
  '/', // Match the root path explicitly
17
17
 
18
- // Match all paths except for API routes, Next.js internals, static assets, favicon, and sitemap.xml
19
- // This prevents middleware from rewriting or interfering with these special paths
20
- '/((?!api|_next/static|_next/image|icon|favicon.ico|sitemap.xml|sitemap-0.xml).*)'
18
+ // Match all paths except for:
19
+ // - API routes
20
+ // - Next.js internals (_next/*)
21
+ // - Static files (*.svg, *.png, *.jpg, *.jpeg, *.gif, *.ico)
22
+ // - Other static assets and special files
23
+ '/((?!api|_next|.*\\.(?:svg|png|jpg|jpeg|gif|ico)|favicon.ico|sitemap.xml|sitemap-0.xml).*)'
21
24
  ]
22
25
  };
@@ -1,12 +1,15 @@
1
1
  'use client';
2
2
 
3
3
  import Link from 'next/link';
4
- import { AppConfig } from '@/base/cases/AppConfig';
5
- import { IOC } from '@/core/IOC';
4
+ import { IOCIdentifier } from '@config/IOCIdentifier';
5
+ import { useIOC } from '@/uikit/hook/useIOC';
6
6
  import { LanguageSwitcher } from './LanguageSwitcher';
7
+ import { LogoutButton } from './LogoutButton';
7
8
  import { ThemeSwitcher } from './ThemeSwitcher';
8
9
 
9
- export function BaseHeader() {
10
+ export function BaseHeader(props: { showLogoutButton?: boolean }) {
11
+ const { showLogoutButton } = props;
12
+ const appConfig = useIOC(IOCIdentifier.AppConfig);
10
13
  return (
11
14
  <header
12
15
  data-testid="base-header"
@@ -28,13 +31,15 @@ export function BaseHeader() {
28
31
  data-testid="base-header-app-name"
29
32
  className="ml-2 text-lg font-semibold text-text"
30
33
  >
31
- {IOC(AppConfig).appName}
34
+ {appConfig.appName}
32
35
  </span>
33
36
  </Link>
34
37
  </div>
35
38
  <div className="flex items-center gap-4">
36
39
  <LanguageSwitcher />
37
40
  <ThemeSwitcher />
41
+
42
+ {showLogoutButton && <LogoutButton />}
38
43
  </div>
39
44
  </div>
40
45
  </header>
@@ -1,11 +1,23 @@
1
1
  'use client';
2
2
  import '@ant-design/v5-patch-for-react-19';
3
+ import { useRouter } from 'next/navigation';
4
+ import { useLocale } from 'next-intl';
3
5
  import { useEffect } from 'react';
6
+ import { I } from '@config/IOCIdentifier';
7
+ import { NavigateBridge } from '@/base/cases/NavigateBridge';
4
8
  import { BootstrapClient } from '@/core/bootstraps/BootstrapClient';
9
+ import { clientIOC } from '@/core/clientIoc/ClientIOC';
5
10
  import { IOCContext } from '../context/IOCContext';
6
11
 
7
12
  export function BootstrapsProvider(props: { children: React.ReactNode }) {
8
- const IOC = BootstrapClient.createSingletonIOC();
13
+ const IOC = clientIOC.create();
14
+ const locale = useLocale();
15
+ const router = useRouter();
16
+
17
+ useEffect(() => {
18
+ IOC(I.RouterServiceInterface).setLocale(locale);
19
+ IOC(NavigateBridge).setUIBridge(router);
20
+ }, [locale, router, IOC]);
9
21
 
10
22
  useEffect(() => {
11
23
  if (typeof window !== 'undefined') {