@qlover/create-app 0.7.7 → 0.7.9
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 +121 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/build/generateLocales.ts +1 -1
- package/dist/templates/next-app/config/IOCIdentifier.ts +15 -2
- package/dist/templates/next-app/config/Identifier/common.error.ts +7 -0
- package/dist/templates/next-app/config/Identifier/page.home.ts +7 -0
- package/dist/templates/next-app/config/i18n/HomeI18n .ts +22 -0
- package/dist/templates/next-app/config/theme.ts +1 -0
- package/dist/templates/next-app/package.json +5 -5
- package/dist/templates/next-app/public/locales/{en/common.json → en.json} +3 -1
- package/dist/templates/next-app/public/locales/{zh/common.json → zh.json} +3 -1
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +8 -26
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +9 -18
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +28 -24
- package/dist/templates/next-app/src/app/[locale]/page.tsx +105 -100
- package/dist/templates/next-app/src/base/cases/DialogHandler.ts +92 -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/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/types/PageProps.ts +9 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +2 -39
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +78 -0
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +37 -0
- package/dist/templates/next-app/src/core/{IocRegisterImpl.ts → clientIoc/ClientIOCRegister.ts} +20 -23
- package/dist/templates/next-app/src/core/globals.ts +3 -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 +1 -2
- package/dist/templates/next-app/src/i18n/routing.ts +3 -3
- package/dist/templates/next-app/src/middleware.ts +6 -3
- package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +12 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +26 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +16 -0
- package/dist/templates/next-app/src/styles/css/page.css +4 -3
- package/dist/templates/next-app/src/styles/css/themes/_default.css +1 -0
- package/dist/templates/next-app/src/styles/css/themes/dark.css +1 -0
- package/dist/templates/next-app/src/styles/css/themes/pink.css +1 -0
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +14 -14
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +27 -0
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +13 -1
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +5 -0
- package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +49 -21
- package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +34 -0
- package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +92 -35
- package/dist/templates/next-app/src/uikit/context/IOCContext.ts +9 -2
- package/package.json +1 -1
- package/dist/templates/next-app/plugins/eslint-plugin-testid.mjs +0 -94
- package/dist/templates/next-app/plugins/generateLocalesPlugin.ts +0 -33
- package/dist/templates/next-app/src/core/IOC.ts +0 -58
- package/dist/templates/next-app/src/server/getServerI18n.ts +0 -26
|
@@ -1,106 +1,111 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Button } from 'antd';
|
|
2
|
+
import { i18nConfig } from '@config/i18n';
|
|
3
|
+
import { homeI18n } from '@config/i18n/HomeI18n ';
|
|
4
|
+
import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
|
|
5
|
+
import { ServerAuth } from '@/base/cases/ServerAuth';
|
|
6
|
+
import type { PageParamsProps } from '@/base/types/PageProps';
|
|
7
|
+
import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
8
|
+
import { redirect } from '@/i18n/routing';
|
|
9
|
+
import { BaseLayout } from '@/uikit/components/BaseLayout';
|
|
10
|
+
import type { Metadata } from 'next';
|
|
11
|
+
|
|
12
|
+
// const navigationItems = [
|
|
13
|
+
// {
|
|
14
|
+
// href: '/identifier',
|
|
15
|
+
// titleKey: 'HOME_IDENTIFIER',
|
|
16
|
+
// descriptionKey: 'HOME_IDENTIFIER_DESCRIPTION'
|
|
17
|
+
// }
|
|
18
|
+
// ];
|
|
19
|
+
|
|
20
|
+
// Generate static params for all supported locales (used for SSG)
|
|
21
|
+
export async function generateStaticParams() {
|
|
22
|
+
// Return one entry for each supported locale
|
|
23
|
+
return i18nConfig.supportedLngs.map((locale) => ({ locale }));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Allow Next.js to statically generate this page if possible (default behavior)
|
|
27
|
+
export const dynamic = 'auto'; // Enable static generation when possible, fallback to dynamic if needed
|
|
28
|
+
|
|
29
|
+
// Optional: Use revalidate if you want ISR (Incremental Static Regeneration)
|
|
30
|
+
// export const revalidate = 3600; // Rebuild every hour (optional)
|
|
31
|
+
|
|
32
|
+
// Generate localized SEO metadata per locale (Next.js 15+ best practice)
|
|
33
|
+
export async function generateMetadata({
|
|
34
|
+
params
|
|
35
|
+
}: {
|
|
36
|
+
params: Promise<PageParamsType>;
|
|
37
|
+
}): Promise<Metadata> {
|
|
38
|
+
const pageParams = new PageParams(await params);
|
|
39
|
+
return await pageParams.getI18nInterface(homeI18n);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default async function Home({ params }: PageParamsProps) {
|
|
43
|
+
const server = new BootstrapServer();
|
|
44
|
+
const pageParams = new PageParams(await params!);
|
|
45
|
+
const locale = pageParams.getLocale();
|
|
46
|
+
const tt = await pageParams.getI18nInterface(homeI18n);
|
|
47
|
+
|
|
48
|
+
if (!(await new ServerAuth(server).hasAuth())) {
|
|
49
|
+
return redirect({ href: '/login', locale });
|
|
50
|
+
}
|
|
2
51
|
|
|
3
|
-
export default async function Home() {
|
|
4
52
|
return (
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
className="
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
53
|
+
<BaseLayout data-testid="HomePage">
|
|
54
|
+
{/* Hero Section */}
|
|
55
|
+
<section className="py-16 px-4">
|
|
56
|
+
<div className="max-w-4xl mx-auto text-center">
|
|
57
|
+
<h1 className="text-4xl md:text-5xl font-bold mb-6 text-text">
|
|
58
|
+
{tt.welcome}
|
|
59
|
+
</h1>
|
|
60
|
+
<p className="text-xl text-text-secondary mb-8">{tt.description}</p>
|
|
61
|
+
</div>
|
|
62
|
+
</section>
|
|
63
|
+
|
|
64
|
+
{/* Navigation Grid */}
|
|
65
|
+
<section className="max-w-6xl mx-auto px-4 py-12">
|
|
66
|
+
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
67
|
+
{/* {navigationItems.map((item) => (
|
|
68
|
+
<LocaleLink
|
|
69
|
+
data-testid={`HomePage-navigation-${item.href}`}
|
|
70
|
+
key={item.href}
|
|
71
|
+
title={item.titleKey}
|
|
72
|
+
className={clsx(
|
|
73
|
+
href={item.href}
|
|
74
|
+
'block rounded-lg p-6',
|
|
75
|
+
'bg-secondary',
|
|
76
|
+
'border border-border',
|
|
77
|
+
'hover:bg-elevated',
|
|
78
|
+
'transition-colors duration-200'
|
|
79
|
+
)}
|
|
80
|
+
>
|
|
81
|
+
<h3 className={`text-xl font-semibold mb-3 text-text`}>
|
|
82
|
+
{t(item.titleKey)}
|
|
83
|
+
</h3>
|
|
84
|
+
<p className="text-text-secondary mb-4">
|
|
85
|
+
{t(item.descriptionKey)}
|
|
86
|
+
</p>
|
|
87
|
+
<Button type="primary" className="w-full">
|
|
88
|
+
{t(i18nKeys.HOME_EXPLORE)}
|
|
89
|
+
</Button>
|
|
90
|
+
</LocaleLink>
|
|
91
|
+
))} */}
|
|
92
|
+
</div>
|
|
93
|
+
</section>
|
|
30
94
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
height={20}
|
|
44
|
-
/>
|
|
45
|
-
Deploy now
|
|
46
|
-
</a>
|
|
47
|
-
<a
|
|
48
|
-
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
|
49
|
-
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
50
|
-
target="_blank"
|
|
51
|
-
rel="noopener noreferrer"
|
|
52
|
-
>
|
|
53
|
-
Read our docs
|
|
54
|
-
</a>
|
|
95
|
+
{/* Call to Action Section */}
|
|
96
|
+
<section className="py-16 px-4 bg-elevated">
|
|
97
|
+
<div className="max-w-4xl mx-auto text-center">
|
|
98
|
+
<h2 className="text-3xl font-bold mb-4 text-text">
|
|
99
|
+
{tt.getStartedTitle}
|
|
100
|
+
</h2>
|
|
101
|
+
<p className="text-lg text-text-secondary mb-8">
|
|
102
|
+
{tt.getStartedDescription}
|
|
103
|
+
</p>
|
|
104
|
+
<Button type="primary" size="large" className="px-8">
|
|
105
|
+
{tt.getStartedButton}
|
|
106
|
+
</Button>
|
|
55
107
|
</div>
|
|
56
|
-
</
|
|
57
|
-
|
|
58
|
-
<a
|
|
59
|
-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
60
|
-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
61
|
-
target="_blank"
|
|
62
|
-
rel="noopener noreferrer"
|
|
63
|
-
>
|
|
64
|
-
<Image
|
|
65
|
-
aria-hidden
|
|
66
|
-
src="/file.svg"
|
|
67
|
-
alt="File icon"
|
|
68
|
-
width={16}
|
|
69
|
-
height={16}
|
|
70
|
-
/>
|
|
71
|
-
Learn
|
|
72
|
-
</a>
|
|
73
|
-
<a
|
|
74
|
-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
75
|
-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
76
|
-
target="_blank"
|
|
77
|
-
rel="noopener noreferrer"
|
|
78
|
-
>
|
|
79
|
-
<Image
|
|
80
|
-
aria-hidden
|
|
81
|
-
src="/window.svg"
|
|
82
|
-
alt="Window icon"
|
|
83
|
-
width={16}
|
|
84
|
-
height={16}
|
|
85
|
-
/>
|
|
86
|
-
Examples
|
|
87
|
-
</a>
|
|
88
|
-
<a
|
|
89
|
-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
90
|
-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
91
|
-
target="_blank"
|
|
92
|
-
rel="noopener noreferrer"
|
|
93
|
-
>
|
|
94
|
-
<Image
|
|
95
|
-
aria-hidden
|
|
96
|
-
src="/globe.svg"
|
|
97
|
-
alt="Globe icon"
|
|
98
|
-
width={16}
|
|
99
|
-
height={16}
|
|
100
|
-
/>
|
|
101
|
-
Go to nextjs.org →
|
|
102
|
-
</a>
|
|
103
|
-
</footer>
|
|
104
|
-
</div>
|
|
108
|
+
</section>
|
|
109
|
+
</BaseLayout>
|
|
105
110
|
);
|
|
106
111
|
}
|
|
@@ -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,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,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,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
|
|
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
|
|