@qlover/create-app 0.7.6 → 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.
- package/CHANGELOG.md +328 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/.env.template +22 -0
- package/dist/templates/next-app/.prettierignore +58 -0
- package/dist/templates/next-app/README.md +36 -0
- package/dist/templates/next-app/build/generateLocales.ts +25 -0
- package/dist/templates/next-app/config/IOCIdentifier.ts +58 -0
- package/dist/templates/next-app/config/Identifier/common.error.ts +41 -0
- package/dist/templates/next-app/config/Identifier/common.ts +62 -0
- package/dist/templates/next-app/config/Identifier/index.ts +10 -0
- package/dist/templates/next-app/config/Identifier/page.about.ts +181 -0
- package/dist/templates/next-app/config/Identifier/page.executor.ts +272 -0
- package/dist/templates/next-app/config/Identifier/page.home.ts +63 -0
- package/dist/templates/next-app/config/Identifier/page.identifiter.ts +39 -0
- package/dist/templates/next-app/config/Identifier/page.jsonStorage.ts +72 -0
- package/dist/templates/next-app/config/Identifier/page.login.ts +165 -0
- package/dist/templates/next-app/config/Identifier/page.register.ts +147 -0
- package/dist/templates/next-app/config/Identifier/page.request.ts +182 -0
- package/dist/templates/next-app/config/common.ts +34 -0
- package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +51 -0
- package/dist/templates/next-app/config/i18n/i18nConfig.ts +12 -0
- package/dist/templates/next-app/config/i18n/index.ts +3 -0
- package/dist/templates/next-app/config/i18n/loginI18n.ts +42 -0
- package/dist/templates/next-app/config/theme.ts +23 -0
- package/dist/templates/next-app/docs/env.md +94 -0
- package/dist/templates/next-app/eslint.config.mjs +181 -0
- package/dist/templates/next-app/next.config.ts +21 -0
- package/dist/templates/next-app/package.json +58 -0
- package/dist/templates/next-app/postcss.config.mjs +5 -0
- package/dist/templates/next-app/public/file.svg +1 -0
- package/dist/templates/next-app/public/globe.svg +1 -0
- package/dist/templates/next-app/public/locales/en.json +184 -0
- package/dist/templates/next-app/public/locales/zh.json +184 -0
- package/dist/templates/next-app/public/next.svg +1 -0
- package/dist/templates/next-app/public/vercel.svg +1 -0
- package/dist/templates/next-app/public/window.svg +1 -0
- package/dist/templates/next-app/src/app/[locale]/favicon.ico +0 -0
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +32 -0
- package/dist/templates/next-app/src/app/[locale]/login/FeatureItem.tsx +13 -0
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +114 -0
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +86 -0
- package/dist/templates/next-app/src/app/[locale]/not-found.tsx +24 -0
- package/dist/templates/next-app/src/app/[locale]/page.tsx +119 -0
- package/dist/templates/next-app/src/base/cases/AppConfig.ts +15 -0
- package/dist/templates/next-app/src/base/cases/DialogHandler.ts +92 -0
- package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +33 -0
- package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +16 -0
- package/dist/templates/next-app/src/base/cases/PageParams.ts +74 -0
- package/dist/templates/next-app/src/base/cases/RouterService.ts +35 -0
- package/dist/templates/next-app/src/base/cases/ServerAuth.ts +17 -0
- package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +27 -0
- package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +25 -0
- package/dist/templates/next-app/src/base/port/IOCInterface.ts +24 -0
- package/dist/templates/next-app/src/base/port/ParamsHandlerInterface.ts +11 -0
- package/dist/templates/next-app/src/base/port/RouterInterface.ts +11 -0
- package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +3 -0
- package/dist/templates/next-app/src/base/port/ServerInterface.ts +12 -0
- package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +11 -0
- package/dist/templates/next-app/src/base/services/I18nService.ts +115 -0
- package/dist/templates/next-app/src/base/services/UserService.ts +23 -0
- package/dist/templates/next-app/src/base/types/PageProps.ts +9 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +61 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +78 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +47 -0
- package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
- package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +18 -0
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +37 -0
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +97 -0
- package/dist/templates/next-app/src/core/globals.ts +24 -0
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +52 -0
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +63 -0
- package/dist/templates/next-app/src/i18n/request.ts +21 -0
- package/dist/templates/next-app/src/i18n/routing.ts +30 -0
- package/dist/templates/next-app/src/middleware.ts +25 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +239 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +178 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +34 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +204 -0
- package/dist/templates/next-app/src/styles/css/index.css +6 -0
- package/dist/templates/next-app/src/styles/css/page.css +19 -0
- package/dist/templates/next-app/src/styles/css/tailwind.css +5 -0
- package/dist/templates/next-app/src/styles/css/themes/_default.css +29 -0
- package/dist/templates/next-app/src/styles/css/themes/dark.css +29 -0
- package/dist/templates/next-app/src/styles/css/themes/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/themes/pink.css +29 -0
- package/dist/templates/next-app/src/styles/css/zIndex.css +9 -0
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +47 -0
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +37 -0
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +50 -0
- package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +52 -0
- package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +51 -0
- package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +34 -0
- package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +21 -0
- package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +99 -0
- package/dist/templates/next-app/src/uikit/context/IOCContext.ts +13 -0
- package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +28 -0
- package/dist/templates/next-app/src/uikit/hook/useIOC.ts +37 -0
- package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +11 -0
- package/dist/templates/next-app/src/uikit/hook/useStore.ts +15 -0
- package/dist/templates/next-app/tailwind.config.ts +8 -0
- package/dist/templates/next-app/tsconfig.json +36 -0
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AntdStaticApiInterface,
|
|
3
|
+
MessageApi,
|
|
4
|
+
ModalApi,
|
|
5
|
+
NotificationApi
|
|
6
|
+
} from '@brain-toolkit/antd-theme-override/react';
|
|
7
|
+
import type {
|
|
8
|
+
UIDialogInterface,
|
|
9
|
+
NotificationOptions
|
|
10
|
+
} from '@qlover/corekit-bridge';
|
|
11
|
+
import type { ModalFuncProps } from 'antd';
|
|
12
|
+
|
|
13
|
+
export interface DialogHandlerOptions
|
|
14
|
+
extends NotificationOptions,
|
|
15
|
+
ModalFuncProps {
|
|
16
|
+
content: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Dialog Handler Implementation
|
|
21
|
+
*
|
|
22
|
+
* Implements the InteractionHubInterface using Ant Design components.
|
|
23
|
+
* Provides concrete implementations for displaying notifications and confirmation dialogs.
|
|
24
|
+
*
|
|
25
|
+
* Features:
|
|
26
|
+
* - Uses Ant Design's message component for notifications
|
|
27
|
+
* - Uses Ant Design's Modal component for confirmations
|
|
28
|
+
* - Supports customizable display durations
|
|
29
|
+
* - Handles error objects appropriately
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const dialog = new DialogHandler();
|
|
33
|
+
* dialog.success('Data saved successfully');
|
|
34
|
+
* dialog.confirm({
|
|
35
|
+
* title: 'Confirm Delete',
|
|
36
|
+
* content: 'Are you sure you want to delete this item?',
|
|
37
|
+
* onOk: () => handleDelete(),
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
40
|
+
export class DialogHandler
|
|
41
|
+
implements UIDialogInterface<DialogHandlerOptions>, AntdStaticApiInterface
|
|
42
|
+
{
|
|
43
|
+
protected antds: {
|
|
44
|
+
message?: MessageApi;
|
|
45
|
+
modal?: ModalApi;
|
|
46
|
+
notification?: NotificationApi;
|
|
47
|
+
} = {};
|
|
48
|
+
|
|
49
|
+
setMessage(message: MessageApi): void {
|
|
50
|
+
this.antds.message = message;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setModal(modal: ModalApi): void {
|
|
54
|
+
this.antds.modal = modal;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setNotification(notification: NotificationApi): void {
|
|
58
|
+
this.antds.notification = notification;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Formats error message from various error types
|
|
63
|
+
*/
|
|
64
|
+
protected formatErrorMessage(error: unknown): string {
|
|
65
|
+
if (error instanceof Error) return error.message;
|
|
66
|
+
if (typeof error === 'string') return error;
|
|
67
|
+
return 'An unknown error occurred';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public success(msg: string, options?: NotificationOptions): void {
|
|
71
|
+
this.antds.message?.success({ content: msg, ...options });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public error(msg: string, options?: NotificationOptions): void {
|
|
75
|
+
this.antds.message?.error({
|
|
76
|
+
content: options?.error ? this.formatErrorMessage(options.error) : msg,
|
|
77
|
+
...options
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public info(msg: string, options?: NotificationOptions): void {
|
|
82
|
+
this.antds.message?.info({ content: msg, ...options });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public warn(msg: string, options?: NotificationOptions): void {
|
|
86
|
+
this.antds.message?.warning({ content: msg, ...options });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public confirm(options: DialogHandlerOptions): void {
|
|
90
|
+
this.antds.modal?.confirm(options);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Container } from 'inversify';
|
|
2
|
+
import type { IOCIdentifierMap } from '@config/IOCIdentifier';
|
|
3
|
+
import type {
|
|
4
|
+
IOCContainerInterface,
|
|
5
|
+
ServiceIdentifier
|
|
6
|
+
} from '@qlover/corekit-bridge';
|
|
7
|
+
|
|
8
|
+
export class InversifyContainer implements IOCContainerInterface {
|
|
9
|
+
protected container: Container;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.container = new Container({
|
|
13
|
+
// allow `@injectable` decorator, auto bind injectable classes
|
|
14
|
+
autobind: true,
|
|
15
|
+
// use singleton scope
|
|
16
|
+
defaultScope: 'Singleton'
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
bind<T>(key: ServiceIdentifier<T>, value: T): void {
|
|
21
|
+
this.container.bind<T>(key).toConstantValue(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get<K extends keyof IOCIdentifierMap>(
|
|
25
|
+
serviceIdentifier: K
|
|
26
|
+
): IOCIdentifierMap[K];
|
|
27
|
+
get<T>(serviceIdentifier: ServiceIdentifier<T>): T;
|
|
28
|
+
get<T, K extends keyof IOCIdentifierMap>(
|
|
29
|
+
serviceIdentifier: ServiceIdentifier<T> | K
|
|
30
|
+
): T | IOCIdentifierMap[K] {
|
|
31
|
+
return this.container.get<T>(serviceIdentifier);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { injectable } from 'inversify';
|
|
2
|
+
import type { UIBridgeInterface } from '@qlover/corekit-bridge';
|
|
3
|
+
import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
|
|
4
|
+
|
|
5
|
+
@injectable()
|
|
6
|
+
export class NavigateBridge implements UIBridgeInterface<AppRouterInstance> {
|
|
7
|
+
protected navigate: AppRouterInstance | null = null;
|
|
8
|
+
|
|
9
|
+
setUIBridge(ui: AppRouterInstance): void {
|
|
10
|
+
this.navigate = ui;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getUIBridge(): AppRouterInstance | null {
|
|
14
|
+
return this.navigate;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { notFound } from 'next/navigation';
|
|
2
|
+
import { getMessages, getTranslations } from 'next-intl/server';
|
|
3
|
+
import { i18nConfig } from '@config/i18n';
|
|
4
|
+
import type { ParamsHandlerInterface as ParamsHandlerInterface } from '../port/ParamsHandlerInterface';
|
|
5
|
+
import type { LocaleType, PageI18nInterface } from '@config/i18n';
|
|
6
|
+
|
|
7
|
+
export interface PageWithParams {
|
|
8
|
+
params?: Promise<PageParamsType>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PageParamsType {
|
|
12
|
+
locale?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Handler Page Params
|
|
17
|
+
*/
|
|
18
|
+
export class PageParams implements ParamsHandlerInterface {
|
|
19
|
+
private locale: string | null;
|
|
20
|
+
|
|
21
|
+
constructor(protected readonly params: PageParamsType) {
|
|
22
|
+
this.locale = this.params.locale || i18nConfig.fallbackLng;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public getLocale(defaultLocale?: string): string {
|
|
26
|
+
if (this.locale) {
|
|
27
|
+
return this.locale;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.locale = this.params.locale || defaultLocale || i18nConfig.fallbackLng;
|
|
31
|
+
|
|
32
|
+
return this.locale;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public getI18nWithNotFound(): string {
|
|
36
|
+
const locale = this.getLocale();
|
|
37
|
+
|
|
38
|
+
if (!i18nConfig.supportedLngs.includes(locale as LocaleType)) {
|
|
39
|
+
notFound();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return locale;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public async getI18nMessages(): Promise<Record<string, string>> {
|
|
46
|
+
const locale = this.getLocale();
|
|
47
|
+
|
|
48
|
+
const messages = await getMessages({ locale });
|
|
49
|
+
|
|
50
|
+
return messages;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async getI18nInterface<T extends PageI18nInterface>(
|
|
54
|
+
i18nInterface: T,
|
|
55
|
+
namespace?: string
|
|
56
|
+
): Promise<T> {
|
|
57
|
+
// Load translation messages from the HomePage namespace
|
|
58
|
+
const t = await getTranslations({
|
|
59
|
+
locale: this.getLocale(),
|
|
60
|
+
namespace: namespace
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const result = Object.fromEntries(
|
|
64
|
+
Object.entries(i18nInterface).map(([key, value]) => {
|
|
65
|
+
if (typeof value === 'string') {
|
|
66
|
+
return [key, t(value)];
|
|
67
|
+
}
|
|
68
|
+
return [key, value];
|
|
69
|
+
})
|
|
70
|
+
) as T;
|
|
71
|
+
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -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,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StoreInterface,
|
|
3
|
+
type StoreStateInterface
|
|
4
|
+
} from '@qlover/corekit-bridge';
|
|
5
|
+
import type { i18nConfig } from '@config/i18n';
|
|
6
|
+
|
|
7
|
+
export type SupportedLocale = (typeof i18nConfig.supportedLngs)[number];
|
|
8
|
+
export type SupportedNamespace = typeof i18nConfig.fallbackLng;
|
|
9
|
+
export type I18nServiceLocale = SupportedLocale;
|
|
10
|
+
|
|
11
|
+
export class I18nServiceState implements StoreStateInterface {
|
|
12
|
+
loading: boolean = false;
|
|
13
|
+
constructor(public language: I18nServiceLocale) {}
|
|
14
|
+
}
|
|
15
|
+
export abstract class I18nServiceInterface
|
|
16
|
+
extends StoreInterface<I18nServiceState>
|
|
17
|
+
implements I18nServiceInterface
|
|
18
|
+
{
|
|
19
|
+
abstract t(key: string, params?: Record<string, unknown>): Promise<string>;
|
|
20
|
+
abstract changeLanguage(language: I18nServiceLocale): Promise<void>;
|
|
21
|
+
abstract changeLoading(loading: boolean): void;
|
|
22
|
+
abstract getCurrentLanguage(): Promise<I18nServiceLocale>;
|
|
23
|
+
abstract isValidLanguage(language: string): language is I18nServiceLocale;
|
|
24
|
+
abstract getSupportedLanguages(): I18nServiceLocale[];
|
|
25
|
+
}
|
|
@@ -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,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,11 @@
|
|
|
1
|
+
import { ImagicaAuthService } from '@brain-toolkit/bridge';
|
|
2
|
+
import type { ExecutorPlugin } from '@qlover/fe-corekit';
|
|
3
|
+
|
|
4
|
+
export abstract class UserServiceInterface
|
|
5
|
+
extends ImagicaAuthService
|
|
6
|
+
implements ExecutorPlugin
|
|
7
|
+
{
|
|
8
|
+
readonly pluginName = 'UserService';
|
|
9
|
+
|
|
10
|
+
abstract getToken(): string | null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useLocaleRoutes } from '@config/common';
|
|
2
|
+
import { i18nConfig } from '@config/i18n';
|
|
3
|
+
import {
|
|
4
|
+
I18nServiceInterface,
|
|
5
|
+
I18nServiceState
|
|
6
|
+
} from '../port/I18nServiceInterface';
|
|
7
|
+
import type { I18nServiceLocale } from '../port/I18nServiceInterface';
|
|
8
|
+
import type { useTranslations } from 'next-intl';
|
|
9
|
+
|
|
10
|
+
type TranslationFunction = ReturnType<typeof useTranslations>;
|
|
11
|
+
|
|
12
|
+
export class I18nService extends I18nServiceInterface {
|
|
13
|
+
readonly pluginName = 'I18nService';
|
|
14
|
+
private initialized: boolean = false;
|
|
15
|
+
private pathname: string = '';
|
|
16
|
+
private translator: TranslationFunction | null = null;
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
super(() => new I18nServiceState(i18nConfig.fallbackLng));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private async ensureInitialized(): Promise<void> {
|
|
23
|
+
if (!this.initialized) {
|
|
24
|
+
throw new Error('I18nService not initialized');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setPathname(pathname: string): void {
|
|
29
|
+
this.pathname = pathname;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setTranslator(translator: TranslationFunction): void {
|
|
33
|
+
this.translator = translator;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @override
|
|
38
|
+
*/
|
|
39
|
+
async onBefore(): Promise<void> {
|
|
40
|
+
if (this.initialized) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.initialized = true;
|
|
45
|
+
|
|
46
|
+
// 初始化语言状态
|
|
47
|
+
const currentLang = this.getCurrentLanguageFromPath();
|
|
48
|
+
if (this.isValidLanguage(currentLang)) {
|
|
49
|
+
this.emit({ ...this.state, language: currentLang });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private getCurrentLanguageFromPath(): string {
|
|
54
|
+
const paths = this.pathname.split('/');
|
|
55
|
+
|
|
56
|
+
for (const path of paths) {
|
|
57
|
+
if (this.isValidLanguage(path)) {
|
|
58
|
+
return path;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return i18nConfig.fallbackLng;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override async changeLanguage(language: I18nServiceLocale): Promise<void> {
|
|
66
|
+
try {
|
|
67
|
+
this.changeLoading(true);
|
|
68
|
+
await this.ensureInitialized();
|
|
69
|
+
|
|
70
|
+
// 在这里我们只需要更新状态,因为实际的语言切换会通过路由处理
|
|
71
|
+
this.emit({ ...this.state, language });
|
|
72
|
+
|
|
73
|
+
// 如果不使用本地化路由,则保存语言设置到本地存储
|
|
74
|
+
if (!useLocaleRoutes && typeof window !== 'undefined') {
|
|
75
|
+
window.localStorage.setItem('i18nextLng', language);
|
|
76
|
+
}
|
|
77
|
+
} finally {
|
|
78
|
+
this.changeLoading(false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override changeLoading(loading: boolean): void {
|
|
83
|
+
this.emit({ ...this.state, loading });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
override async getCurrentLanguage(): Promise<I18nServiceLocale> {
|
|
87
|
+
await this.ensureInitialized();
|
|
88
|
+
return this.getCurrentLanguageFromPath() as I18nServiceLocale;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override isValidLanguage(language: string): language is I18nServiceLocale {
|
|
92
|
+
return i18nConfig.supportedLngs.includes(language as I18nServiceLocale);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
override getSupportedLanguages(): I18nServiceLocale[] {
|
|
96
|
+
return [...i18nConfig.supportedLngs];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
override async t(
|
|
100
|
+
key: string,
|
|
101
|
+
params?: Record<string, string | number | Date>
|
|
102
|
+
): Promise<string> {
|
|
103
|
+
await this.ensureInitialized();
|
|
104
|
+
|
|
105
|
+
if (!this.translator) {
|
|
106
|
+
return key;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
return this.translator(key, params);
|
|
111
|
+
} catch {
|
|
112
|
+
return key;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CookieStorage } from '@qlover/corekit-bridge';
|
|
2
|
+
import { injectable, inject } from 'inversify';
|
|
3
|
+
import { AppConfig } from '../cases/AppConfig';
|
|
4
|
+
import { UserServiceInterface } from '../port/UserServiceInterface';
|
|
5
|
+
|
|
6
|
+
@injectable()
|
|
7
|
+
export class UserService extends UserServiceInterface {
|
|
8
|
+
constructor(@inject(AppConfig) protected appConfig: AppConfig) {
|
|
9
|
+
super({
|
|
10
|
+
credentialStorage: {
|
|
11
|
+
key: appConfig.userTokenKey,
|
|
12
|
+
storage: new CookieStorage()
|
|
13
|
+
},
|
|
14
|
+
requestConfig: {
|
|
15
|
+
env: appConfig.env !== 'production' ? 'development' : 'production'
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getToken(): string | null {
|
|
21
|
+
return this.store.getCredential();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Bootstrap } from '@qlover/corekit-bridge';
|
|
2
|
+
import { isObject } from 'lodash';
|
|
3
|
+
import { browserGlobalsName } from '@config/common';
|
|
4
|
+
import { BootstrapsRegistry } from './BootstrapsRegistry';
|
|
5
|
+
import * as globals from '../globals';
|
|
6
|
+
import type { IOCIdentifierMap } from '@config/IOCIdentifier';
|
|
7
|
+
import type {
|
|
8
|
+
IOCContainerInterface,
|
|
9
|
+
IOCFunctionInterface
|
|
10
|
+
} from '@qlover/corekit-bridge';
|
|
11
|
+
|
|
12
|
+
export type BootstrapAppArgs = {
|
|
13
|
+
/**
|
|
14
|
+
* 启动的根节点,通常是window
|
|
15
|
+
*/
|
|
16
|
+
root: unknown;
|
|
17
|
+
/**
|
|
18
|
+
* 当前路径
|
|
19
|
+
*/
|
|
20
|
+
pathname: string;
|
|
21
|
+
/**
|
|
22
|
+
* IOC容器
|
|
23
|
+
*/
|
|
24
|
+
IOC: IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export class BootstrapClient {
|
|
28
|
+
static async main(args: BootstrapAppArgs): Promise<BootstrapAppArgs> {
|
|
29
|
+
const { root, IOC } = args;
|
|
30
|
+
|
|
31
|
+
if (!isObject(root)) {
|
|
32
|
+
throw new Error('root is not an object');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { logger, appConfig } = globals;
|
|
36
|
+
|
|
37
|
+
const bootstrap = new Bootstrap({
|
|
38
|
+
root,
|
|
39
|
+
logger,
|
|
40
|
+
ioc: {
|
|
41
|
+
manager: IOC
|
|
42
|
+
},
|
|
43
|
+
globalOptions: {
|
|
44
|
+
sources: globals,
|
|
45
|
+
target: browserGlobalsName
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await bootstrap.initialize();
|
|
51
|
+
|
|
52
|
+
const bootstrapsRegistry = new BootstrapsRegistry(args);
|
|
53
|
+
|
|
54
|
+
await bootstrap.use(bootstrapsRegistry.register()).start();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
logger.error(`${appConfig.appName} starup error:`, error);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return args;
|
|
60
|
+
}
|
|
61
|
+
}
|