@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.
- package/CHANGELOG.md +141 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/config/IOCIdentifier.ts +2 -2
- package/dist/templates/next-app/config/Identifier/common/common.ts +14 -0
- package/dist/templates/next-app/config/Identifier/pages/index.ts +1 -0
- package/dist/templates/next-app/config/Identifier/pages/page.about.ts +20 -0
- package/dist/templates/next-app/config/common.ts +1 -1
- package/dist/templates/next-app/config/cookies.ts +23 -0
- package/dist/templates/next-app/config/i18n/AboutI18n.ts +14 -0
- package/dist/templates/next-app/config/i18n/i18nConfig.ts +3 -1
- package/dist/templates/next-app/config/i18n/index.ts +1 -0
- package/dist/templates/next-app/config/i18n/loginI18n.ts +8 -0
- package/dist/templates/next-app/config/theme.ts +4 -0
- package/dist/templates/next-app/eslint.config.mjs +4 -1
- package/dist/templates/next-app/next.config.ts +1 -0
- package/dist/templates/next-app/package.json +13 -4
- package/dist/templates/next-app/public/locales/en.json +5 -0
- package/dist/templates/next-app/public/locales/zh.json +5 -0
- package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +37 -0
- package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +30 -6
- package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +47 -10
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +22 -10
- package/dist/templates/next-app/src/app/[locale]/page.tsx +23 -8
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +21 -9
- package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +7 -28
- package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +7 -34
- package/dist/templates/next-app/src/app/api/admin/locales/route.ts +12 -34
- package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +7 -26
- package/dist/templates/next-app/src/app/api/admin/users/route.ts +14 -33
- package/dist/templates/next-app/src/app/api/locales/json/route.ts +13 -25
- package/dist/templates/next-app/src/app/api/user/login/route.ts +6 -46
- package/dist/templates/next-app/src/app/api/user/logout/route.ts +5 -24
- package/dist/templates/next-app/src/app/api/user/register/route.ts +6 -45
- package/dist/templates/next-app/src/app/manifest.ts +16 -0
- package/dist/templates/next-app/src/app/robots.txt +2 -0
- package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +12 -2
- package/dist/templates/next-app/src/base/cases/RouterService.ts +5 -5
- package/dist/templates/next-app/src/base/port/AppApiInterface.ts +22 -0
- package/dist/templates/next-app/src/base/port/IOCInterface.ts +9 -0
- package/dist/templates/next-app/src/base/types/{PageProps.ts → AppPageRouter.ts} +4 -1
- package/dist/templates/next-app/src/base/types/PagesRouter.ts +9 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +19 -5
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +2 -2
- package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +29 -8
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +4 -2
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +25 -7
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +1 -1
- package/dist/templates/next-app/src/i18n/loadMessages.ts +103 -0
- package/dist/templates/next-app/src/i18n/request.ts +3 -22
- package/dist/templates/next-app/src/pages/[locale]/about.tsx +61 -0
- package/dist/templates/next-app/src/pages/_app.tsx +50 -0
- package/dist/templates/next-app/src/pages/_document.tsx +13 -0
- package/dist/templates/next-app/src/{middleware.ts → proxy.ts} +2 -1
- package/dist/templates/next-app/src/server/AppPageRouteParams.ts +94 -0
- package/dist/templates/next-app/src/server/NextApiServer.ts +53 -0
- package/dist/templates/next-app/src/server/PagesRouteParams.ts +136 -0
- package/dist/templates/next-app/src/server/{sqlBridges/SupabaseBridge.ts → SupabaseBridge.ts} +2 -0
- package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +74 -0
- package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +39 -0
- package/dist/templates/next-app/src/server/controllers/LocalesController.ts +33 -0
- package/dist/templates/next-app/src/server/controllers/UserController.ts +77 -0
- package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +8 -0
- package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +21 -0
- package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +11 -0
- package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +10 -0
- package/dist/templates/next-app/src/server/port/{ParamsHandlerInterface.ts → RouteParamsnHandlerInterface.ts} +9 -2
- package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
- package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +8 -0
- package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +1 -1
- package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +2 -2
- package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +8 -2
- package/dist/templates/next-app/src/{base → server}/services/AdminLocalesService.ts +2 -2
- package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +25 -10
- package/dist/templates/next-app/src/server/services/ApiUserService.ts +5 -2
- package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +4 -2
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +1 -1
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +10 -10
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -44
- package/dist/templates/next-app/src/styles/css/index.css +1 -1
- package/dist/templates/next-app/src/styles/css/scrollbar.css +34 -0
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +34 -11
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +69 -39
- package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +64 -0
- package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +42 -0
- package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +34 -0
- package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +17 -0
- package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +112 -0
- package/dist/templates/next-app/src/uikit/{components → components-app}/LanguageSwitcher.tsx +15 -19
- package/dist/templates/next-app/src/uikit/{components → components-app}/ThemeSwitcher.tsx +53 -52
- package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +98 -0
- package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +93 -0
- package/dist/templates/next-app/src/uikit/context/IOCContext.ts +16 -4
- package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +32 -0
- package/dist/templates/next-app/tsconfig.json +3 -2
- package/package.json +1 -1
- package/dist/templates/next-app/src/server/PageParams.ts +0 -66
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +0 -80
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +0 -65
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +0 -58
- package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +0 -21
- /package/dist/templates/next-app/{src/app/[locale] → public}/favicon.ico +0 -0
- /package/dist/templates/next-app/src/uikit/{components → components-app}/LogoutButton.tsx +0 -0
|
@@ -1,27 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
6
|
+
const requestBody = await req.json();
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
+
}
|
|
@@ -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 {
|
|
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(
|
|
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
|
-
|
|
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 }>;
|
|
@@ -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 {
|
|
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>;
|
|
@@ -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 {
|
|
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 {
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
52
|
+
logger.info('ClientIOC register');
|
|
53
|
+
new ClientIOCRegister(options).register(this.ioc.implemention!, this.ioc);
|
|
33
54
|
|
|
34
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
42
|
-
new InversifyContainer()
|
|
43
|
-
);
|
|
46
|
+
this.ioc = createIOCFunction(new InversifyContainer());
|
|
44
47
|
|
|
45
|
-
|
|
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/
|
|
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 {
|
|
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
|
-
|
|
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
|
|
21
|
+
messages,
|
|
41
22
|
// 将 MISSING_MESSAGE 错误转换为警告
|
|
42
23
|
onError: (error) => {
|
|
43
24
|
if (error.message.includes('MISSING_MESSAGE')) {
|