@qlover/create-app 1.0.1 → 1.1.0
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 +30 -0
- package/dist/index.cjs +9 -9
- package/dist/index.js +9 -9
- package/dist/templates/next-app/config/Identifier/api.ts +7 -0
- package/dist/templates/next-app/config/Identifier/pages/page.register.ts +8 -0
- package/dist/templates/next-app/config/common.ts +1 -1
- package/dist/templates/next-app/config/i18n/HomeI18n.ts +2 -0
- package/dist/templates/next-app/config/i18n/register18n.ts +2 -1
- package/dist/templates/next-app/config/route.ts +9 -0
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +1 -1
- package/dist/templates/next-app/next.config.ts +5 -4
- package/dist/templates/next-app/package.json +7 -8
- package/dist/templates/next-app/public/locales/en.json +4 -1
- package/dist/templates/next-app/public/locales/zh.json +4 -1
- package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +18 -0
- package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/LoginForm.tsx +2 -1
- package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/page.tsx +4 -5
- package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +8 -0
- package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/RegisterForm.tsx +24 -3
- package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/page.tsx +4 -5
- package/dist/templates/next-app/src/app/[locale]/page.tsx +18 -45
- package/dist/templates/next-app/src/app/api/ai/completions/route.ts +13 -13
- package/dist/templates/next-app/src/app/api/auth/callback/route.ts +11 -0
- package/dist/templates/next-app/src/app/api/callback/route.ts +49 -0
- package/dist/templates/next-app/src/base/cases/AppConfig.ts +2 -0
- package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +3 -6
- package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -1
- package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +13 -15
- package/dist/templates/next-app/src/base/cases/RouterService.ts +2 -7
- package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -6
- package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +53 -0
- package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -10
- package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -3
- package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +1 -1
- package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -18
- package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +10 -5
- package/dist/templates/next-app/src/base/services/{appApi/AppApiRequester.ts → AppApiRequester.ts} +16 -11
- package/dist/templates/next-app/src/base/services/AppUserGateway.ts +110 -0
- package/dist/templates/next-app/src/base/services/I18nService.ts +1 -7
- package/dist/templates/next-app/src/base/services/ResourceService.ts +1 -4
- package/dist/templates/next-app/src/base/services/UserService.ts +28 -17
- package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +5 -7
- package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +4 -3
- package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +24 -16
- package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +2 -5
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +15 -18
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -5
- package/dist/templates/next-app/src/core/globals.ts +1 -0
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +2 -8
- package/dist/templates/next-app/src/i18n/routing.ts +8 -3
- package/dist/templates/next-app/src/lib/supabase/client.ts +8 -0
- package/dist/templates/next-app/src/lib/supabase/conts.ts +2 -0
- package/dist/templates/next-app/src/lib/supabase/proxy.ts +84 -0
- package/dist/templates/next-app/src/lib/supabase/server.ts +38 -0
- package/dist/templates/next-app/src/proxy.ts +8 -1
- package/dist/templates/next-app/src/server/AppPageRouteParams.ts +5 -2
- package/dist/templates/next-app/src/server/NextApiServer.ts +2 -9
- package/dist/templates/next-app/src/server/PagesRouteParams.ts +3 -4
- package/dist/templates/next-app/src/server/ServerAuth.ts +18 -12
- package/dist/templates/next-app/src/server/SupabaseBridge.ts +66 -59
- package/dist/templates/next-app/src/server/controllers/UserController.ts +7 -2
- package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +4 -0
- package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -1
- package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +7 -1
- package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -3
- package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -3
- package/dist/templates/next-app/src/server/services/UserService.ts +71 -51
- package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -3
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -6
- package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +68 -0
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +3 -3
- package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -6
- package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +29 -0
- package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +21 -28
- package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +20 -0
- package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +2 -2
- package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +3 -17
- package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +8 -0
- package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +42 -0
- package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +2 -0
- package/package.json +2 -2
- package/dist/templates/next-app/src/base/cases/TranslateI18nInterface.ts +0 -25
- package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +0 -78
- package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +0 -25
- package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +0 -78
- package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +0 -8
|
@@ -167,3 +167,11 @@ export const PAGE_REGISTER_LOGIN_LINK = 'page_register:login_link';
|
|
|
167
167
|
* @localEn Please agree to the Terms of Service and Privacy Policy
|
|
168
168
|
*/
|
|
169
169
|
export const PAGE_REGISTER_TERMS_REQUIRED = 'page_register:terms_required';
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @description 注册后需要验证邮箱
|
|
173
|
+
* @localZh 请查收邮箱完成账号注册
|
|
174
|
+
* @localEn Please check your email to complete the registration
|
|
175
|
+
*/
|
|
176
|
+
export const PAGE_REGISTER_EMAIL_VERIFY_TITLE =
|
|
177
|
+
'page_register:email_verify_title';
|
|
@@ -26,7 +26,7 @@ export const useLocaleRoutes = true;
|
|
|
26
26
|
* - true: 使用API获取本地化数据,可以在 /admin/locales 页面中对他进行修改
|
|
27
27
|
* - false: 不使用API获取本地化数据,直接使用 `@brain-toolkit/ts2locales` 生成的json数据
|
|
28
28
|
*/
|
|
29
|
-
export const useApiLocales =
|
|
29
|
+
export const useApiLocales = false;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* 是否在在 useWarnTranslations 中警告缺失的翻译,而不是抛出错误
|
|
@@ -8,6 +8,8 @@ import * as i18nKeys from '../Identifier/pages/page.home';
|
|
|
8
8
|
*/
|
|
9
9
|
export type HomeI18nInterface = typeof homeI18n;
|
|
10
10
|
|
|
11
|
+
export const homeI18nNamespace = 'page_home';
|
|
12
|
+
|
|
11
13
|
export const homeI18n = Object.freeze({
|
|
12
14
|
// basic meta properties
|
|
13
15
|
title: i18nKeys.PAGE_HOME_TITLE,
|
|
@@ -39,5 +39,6 @@ export const register18n = Object.freeze({
|
|
|
39
39
|
have_account: i18nKeys.PAGE_REGISTER_HAVE_ACCOUNT,
|
|
40
40
|
confirm_password: i18nKeys.PAGE_REGISTER_CONFIRM_PASSWORD,
|
|
41
41
|
terms_required: i18nKeys.PAGE_REGISTER_TERMS_REQUIRED,
|
|
42
|
-
login_link: i18nKeys.PAGE_REGISTER_LOGIN_LINK
|
|
42
|
+
login_link: i18nKeys.PAGE_REGISTER_LOGIN_LINK,
|
|
43
|
+
email_verify: i18nKeys.PAGE_REGISTER_EMAIL_VERIFY_TITLE
|
|
43
44
|
});
|
|
@@ -9,7 +9,7 @@ export const UserRole = {
|
|
|
9
9
|
export type UserRoleType = (typeof UserRole)[keyof typeof UserRole];
|
|
10
10
|
|
|
11
11
|
export const userSchema = z.object({
|
|
12
|
-
id: z.
|
|
12
|
+
id: z.string(),
|
|
13
13
|
role: z.nativeEnum(UserRole),
|
|
14
14
|
email: z.string().email(),
|
|
15
15
|
password: z.string(),
|
|
@@ -12,10 +12,11 @@ generateLocales().catch((error) => {
|
|
|
12
12
|
const nextConfig: NextConfig = {
|
|
13
13
|
// reactStrictMode: false,
|
|
14
14
|
// turbopack 在接下本地 file: 依赖时支持还不够好
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
turbopack: {
|
|
16
|
+
root: __dirname // 明确指定根目录
|
|
17
|
+
},
|
|
18
|
+
// pages 路由需要
|
|
19
|
+
transpilePackages: ['@qlover/fe-corekit', '@qlover/corekit-bridge'],
|
|
19
20
|
env: {
|
|
20
21
|
APP_ENV: process.env.APP_ENV
|
|
21
22
|
}
|
|
@@ -22,13 +22,12 @@
|
|
|
22
22
|
"@ant-design/icons": "^6.0.0",
|
|
23
23
|
"@ant-design/nextjs-registry": "^1.3.0",
|
|
24
24
|
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
|
25
|
-
"@brain-toolkit/antd-blocks": "^0.
|
|
25
|
+
"@brain-toolkit/antd-blocks": "^0.1.2",
|
|
26
26
|
"@brain-toolkit/antd-theme-override": "^0.0.3",
|
|
27
|
-
"@brain-toolkit/
|
|
28
|
-
"@
|
|
29
|
-
"@qlover/corekit
|
|
30
|
-
"@qlover/
|
|
31
|
-
"@qlover/logger": "latest",
|
|
27
|
+
"@brain-toolkit/react-kit": "^0.2.2",
|
|
28
|
+
"@qlover/corekit-bridge": "^2.1.1",
|
|
29
|
+
"@qlover/fe-corekit": "^3.1.1",
|
|
30
|
+
"@qlover/logger": "^1.0.0",
|
|
32
31
|
"@qlover/slice-store-react": "^1.4.1",
|
|
33
32
|
"@supabase/postgrest-js": "^2.87.1",
|
|
34
33
|
"@supabase/ssr": "^0.7.0",
|
|
@@ -41,7 +40,7 @@
|
|
|
41
40
|
"inversify": "^7.8.1",
|
|
42
41
|
"jsonwebtoken": "^9.0.2",
|
|
43
42
|
"lodash": "^4.17.21",
|
|
44
|
-
"next": "^16.
|
|
43
|
+
"next": "^16.1.6",
|
|
45
44
|
"next-intl": "^4.5.8",
|
|
46
45
|
"next-themes": "^0.4.6",
|
|
47
46
|
"openai": "^5.23.0",
|
|
@@ -55,7 +54,7 @@
|
|
|
55
54
|
"@brain-toolkit/ts2locales": "^0.2.3",
|
|
56
55
|
"@eslint/eslintrc": "^3",
|
|
57
56
|
"@qlover/env-loader": "^0.3.0",
|
|
58
|
-
"@qlover/eslint-plugin": "
|
|
57
|
+
"@qlover/eslint-plugin": "^2.0.0",
|
|
59
58
|
"@tailwindcss/postcss": "^4",
|
|
60
59
|
"@types/lodash": "^4.17.20",
|
|
61
60
|
"@types/ms": "^2.1.0",
|
|
@@ -175,5 +175,8 @@
|
|
|
175
175
|
"page_register:privacy_link": "Privacy Policy",
|
|
176
176
|
"page_register:have_account": "Already have an account?",
|
|
177
177
|
"page_register:login_link": "Sign in",
|
|
178
|
-
"page_register:terms_required": "Please agree to the Terms of Service and Privacy Policy"
|
|
178
|
+
"page_register:terms_required": "Please agree to the Terms of Service and Privacy Policy",
|
|
179
|
+
"api:user__not_verfified": "User not verified",
|
|
180
|
+
"page_register:email_verify_title": "Please check your email to complete the registration",
|
|
181
|
+
"page_home:welcome_with_user": "Welcome {email} to the homepage"
|
|
179
182
|
}
|
|
@@ -175,5 +175,8 @@
|
|
|
175
175
|
"page_register:privacy_link": "隐私政策",
|
|
176
176
|
"page_register:have_account": "已有账号?",
|
|
177
177
|
"page_register:login_link": "登录",
|
|
178
|
-
"page_register:terms_required": "请同意服务条款和隐私政策"
|
|
178
|
+
"page_register:terms_required": "请同意服务条款和隐私政策",
|
|
179
|
+
"api:user__not_verfified": "用户未验证",
|
|
180
|
+
"page_register:email_verify_title": "请查收邮箱完成账号注册",
|
|
181
|
+
"page_home:welcome_with_user": "欢迎 {email} 来到主页"
|
|
179
182
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PageLayoutProps } from '@/base/types/AppPageRouter';
|
|
2
|
+
import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
3
|
+
import { redirect } from '@/i18n/routing';
|
|
4
|
+
import { AppPageRouteParams } from '@/server/AppPageRouteParams';
|
|
5
|
+
import { ServerAuth } from '@/server/ServerAuth';
|
|
6
|
+
|
|
7
|
+
export default async function AuthRootPage(props: PageLayoutProps) {
|
|
8
|
+
const pageParams = new AppPageRouteParams(await props.params!);
|
|
9
|
+
const locale = pageParams.getLocale();
|
|
10
|
+
|
|
11
|
+
if (await bootstrapServer.getIOC(ServerAuth).hasAuth()) {
|
|
12
|
+
console.info('> User already logged in, redirecting to home page');
|
|
13
|
+
|
|
14
|
+
return redirect({ href: '/', locale: locale });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return <>{props.children}</>;
|
|
18
|
+
}
|
|
@@ -9,6 +9,7 @@ import { useIOC } from '@/uikit/hook/useIOC';
|
|
|
9
9
|
import { useWarnTranslations } from '@/uikit/hook/useWarnTranslations';
|
|
10
10
|
import type { LoginI18nInterface } from '@config/i18n/loginI18n';
|
|
11
11
|
import { I } from '@config/IOCIdentifier';
|
|
12
|
+
import { ROUTE_REGISTER } from '@config/route';
|
|
12
13
|
|
|
13
14
|
interface LoginFormData {
|
|
14
15
|
email: string;
|
|
@@ -116,7 +117,7 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
|
|
|
116
117
|
|
|
117
118
|
<div className="text-center mt-6">
|
|
118
119
|
<span className="text-text-tertiary">{tt.noAccount} </span>
|
|
119
|
-
<LocaleLink href=
|
|
120
|
+
<LocaleLink href={ROUTE_REGISTER} title={tt.createAccountTitle}>
|
|
120
121
|
{tt.createAccount}
|
|
121
122
|
</LocaleLink>
|
|
122
123
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { notFound } from 'next/navigation';
|
|
2
2
|
import type { PageParamsProps } from '@/base/types/AppPageRouter';
|
|
3
|
-
import {
|
|
3
|
+
import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
4
4
|
import { redirect } from '@/i18n/routing';
|
|
5
5
|
import {
|
|
6
6
|
AppPageRouteParams,
|
|
@@ -11,6 +11,7 @@ import { FeatureItem } from '@/uikit/components/FeatureItem';
|
|
|
11
11
|
import { AppRoutePage } from '@/uikit/components-app/AppRoutePage';
|
|
12
12
|
import { loginI18n, i18nConfig, NS_PAGE_LOGIN } from '@config/i18n';
|
|
13
13
|
import { COMMON_ADMIN_TITLE } from '@config/Identifier';
|
|
14
|
+
import { ROUTE_LOGIN } from '@config/route';
|
|
14
15
|
import { LoginForm } from './LoginForm';
|
|
15
16
|
import type { Metadata } from 'next';
|
|
16
17
|
|
|
@@ -45,9 +46,7 @@ export default async function LoginPage(props: PageParamsProps) {
|
|
|
45
46
|
const params = await props.params;
|
|
46
47
|
const pageParams = new AppPageRouteParams(params);
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (await server.getIOC(ServerAuth).hasAuth()) {
|
|
49
|
+
if (await bootstrapServer.getIOC(ServerAuth).hasAuth()) {
|
|
51
50
|
return redirect({ href: '/', locale: params.locale! });
|
|
52
51
|
}
|
|
53
52
|
|
|
@@ -63,7 +62,7 @@ export default async function LoginPage(props: PageParamsProps) {
|
|
|
63
62
|
title: tt.title,
|
|
64
63
|
adminTitle: tt.adminTitle
|
|
65
64
|
}}
|
|
66
|
-
headerHref=
|
|
65
|
+
headerHref={ROUTE_LOGIN}
|
|
67
66
|
mainProps={{
|
|
68
67
|
className: 'text-xs1 bg-primary flex min-h-screen'
|
|
69
68
|
}}
|
package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/RegisterForm.tsx
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { LockOutlined, MailOutlined, UserOutlined } from '@ant-design/icons';
|
|
4
|
+
import { useStore } from '@brain-toolkit/react-kit';
|
|
4
5
|
import { Button, Checkbox, Form, Input } from 'antd';
|
|
5
6
|
import { useState } from 'react';
|
|
6
7
|
import { useIOC } from '@/uikit/hook/useIOC';
|
|
@@ -13,12 +14,20 @@ export function RegisterForm(props: { tt: RegisterI18nInterface }) {
|
|
|
13
14
|
const logger = useIOC(I.Logger);
|
|
14
15
|
const routerService = useIOC(I.RouterServiceInterface);
|
|
15
16
|
const [loading, setLoading] = useState(false);
|
|
17
|
+
const result = useStore(userService.getUIStore(), (state) => state.result);
|
|
16
18
|
|
|
17
|
-
const handleRegister = async (values:
|
|
19
|
+
const handleRegister = async (values: {
|
|
20
|
+
username: string;
|
|
21
|
+
email: string;
|
|
22
|
+
password: string;
|
|
23
|
+
}) => {
|
|
18
24
|
try {
|
|
19
25
|
setLoading(true);
|
|
20
|
-
await userService.register(
|
|
21
|
-
|
|
26
|
+
await userService.register({
|
|
27
|
+
username: values.username,
|
|
28
|
+
email: values.email,
|
|
29
|
+
password: values.password
|
|
30
|
+
});
|
|
22
31
|
} catch (error) {
|
|
23
32
|
logger.error(error);
|
|
24
33
|
} finally {
|
|
@@ -39,7 +48,19 @@ export function RegisterForm(props: { tt: RegisterI18nInterface }) {
|
|
|
39
48
|
layout="vertical"
|
|
40
49
|
className="space-y-4"
|
|
41
50
|
validateTrigger="onSubmit"
|
|
51
|
+
disabled={!!result}
|
|
52
|
+
initialValues={{
|
|
53
|
+
username: 'myused@sina.com',
|
|
54
|
+
email: 'myused@sina.com',
|
|
55
|
+
password: 'q1234566',
|
|
56
|
+
confirmPassword: 'q1234566',
|
|
57
|
+
agreeToTerms: true
|
|
58
|
+
}}
|
|
42
59
|
>
|
|
60
|
+
{result && result.email_confirmed_at == null ? (
|
|
61
|
+
<div className="text-xl text-red-500">{tt.email_verify}</div>
|
|
62
|
+
) : null}
|
|
63
|
+
|
|
43
64
|
<Form.Item
|
|
44
65
|
name="username"
|
|
45
66
|
rules={[
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { notFound } from 'next/navigation';
|
|
2
2
|
import type { PageParamsProps } from '@/base/types/AppPageRouter';
|
|
3
|
-
import {
|
|
3
|
+
import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
4
4
|
import { redirect } from '@/i18n/routing';
|
|
5
5
|
import {
|
|
6
6
|
AppPageRouteParams,
|
|
@@ -45,9 +45,7 @@ export default async function LoginPage(props: PageParamsProps) {
|
|
|
45
45
|
const params = await props.params;
|
|
46
46
|
const pageParams = new AppPageRouteParams(params);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (await server.getIOC(ServerAuth).hasAuth()) {
|
|
48
|
+
if (await bootstrapServer.getIOC(ServerAuth).hasAuth()) {
|
|
51
49
|
return redirect({ href: '/', locale: params.locale! });
|
|
52
50
|
}
|
|
53
51
|
|
|
@@ -63,7 +61,8 @@ export default async function LoginPage(props: PageParamsProps) {
|
|
|
63
61
|
title: tt.title,
|
|
64
62
|
adminTitle: tt.adminTitle
|
|
65
63
|
}}
|
|
66
|
-
|
|
64
|
+
showAuthButton={false}
|
|
65
|
+
headerHref=""
|
|
67
66
|
mainProps={{
|
|
68
67
|
className: 'text-xs1 bg-primary flex min-h-screen'
|
|
69
68
|
}}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { Button } from 'antd';
|
|
2
2
|
import type { PageParamsProps } from '@/base/types/AppPageRouter';
|
|
3
|
-
import {
|
|
4
|
-
import { redirect } from '@/i18n/routing';
|
|
3
|
+
import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
5
4
|
import {
|
|
6
5
|
AppPageRouteParams,
|
|
7
6
|
type PageParamsType
|
|
8
7
|
} from '@/server/AppPageRouteParams';
|
|
9
8
|
import { ServerAuth } from '@/server/ServerAuth';
|
|
10
9
|
import { AppRoutePage } from '@/uikit/components-app/AppRoutePage';
|
|
11
|
-
import { i18nConfig, homeI18n } from '@config/i18n';
|
|
10
|
+
import { i18nConfig, homeI18n, homeI18nNamespace } from '@config/i18n';
|
|
12
11
|
import { COMMON_ADMIN_TITLE } from '@config/Identifier';
|
|
13
12
|
import type { Metadata } from 'next';
|
|
14
13
|
|
|
@@ -43,27 +42,27 @@ export async function generateMetadata({
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
export default async function Home({ params }: PageParamsProps) {
|
|
46
|
-
const server = new BootstrapServer();
|
|
47
45
|
const pageParams = new AppPageRouteParams(await params!);
|
|
48
|
-
const
|
|
49
|
-
const tt = await pageParams.getI18nInterface({
|
|
50
|
-
...homeI18n,
|
|
51
|
-
adminTitle: COMMON_ADMIN_TITLE
|
|
52
|
-
});
|
|
46
|
+
const user = await bootstrapServer.getIOC(ServerAuth).getUser();
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
// const locale = pageParams.getLocale();
|
|
49
|
+
const tt = await pageParams.getI18nInterface(
|
|
50
|
+
{
|
|
51
|
+
...homeI18n,
|
|
52
|
+
adminTitle: COMMON_ADMIN_TITLE
|
|
53
|
+
},
|
|
54
|
+
homeI18nNamespace
|
|
55
|
+
);
|
|
57
56
|
|
|
58
57
|
return (
|
|
59
58
|
<AppRoutePage
|
|
60
59
|
data-testid="AppRoute-HomePage"
|
|
61
|
-
showLogoutButton
|
|
62
|
-
showAdminButton
|
|
63
60
|
tt={{
|
|
64
61
|
title: tt.title,
|
|
65
62
|
adminTitle: tt.adminTitle
|
|
66
63
|
}}
|
|
64
|
+
showAdminButton
|
|
65
|
+
showAuthButton
|
|
67
66
|
>
|
|
68
67
|
{/* Hero Section */}
|
|
69
68
|
<section className="py-16 px-4">
|
|
@@ -71,41 +70,15 @@ export default async function Home({ params }: PageParamsProps) {
|
|
|
71
70
|
<h1 className="text-4xl md:text-5xl font-bold mb-6 text-text">
|
|
72
71
|
{tt.welcome}
|
|
73
72
|
</h1>
|
|
73
|
+
{!!user ? (
|
|
74
|
+
<p data-testid="AuthUserEmail" className="text-lg text-text">
|
|
75
|
+
{user.email}
|
|
76
|
+
</p>
|
|
77
|
+
) : null}
|
|
74
78
|
<p className="text-xl text-text-secondary mb-8">{tt.description}</p>
|
|
75
79
|
</div>
|
|
76
80
|
</section>
|
|
77
81
|
|
|
78
|
-
{/* Navigation Grid */}
|
|
79
|
-
<section className="max-w-6xl mx-auto px-4 py-12">
|
|
80
|
-
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
81
|
-
{/* {navigationItems.map((item) => (
|
|
82
|
-
<LocaleLink
|
|
83
|
-
data-testid={`HomePage-navigation-${item.href}`}
|
|
84
|
-
key={item.href}
|
|
85
|
-
title={item.titleKey}
|
|
86
|
-
className={clsx(
|
|
87
|
-
href={item.href}
|
|
88
|
-
'block rounded-lg p-6',
|
|
89
|
-
'bg-secondary',
|
|
90
|
-
'border border-border',
|
|
91
|
-
'hover:bg-elevated',
|
|
92
|
-
'transition-colors duration-200'
|
|
93
|
-
)}
|
|
94
|
-
>
|
|
95
|
-
<h3 className={`text-xl font-semibold mb-3 text-text`}>
|
|
96
|
-
{t(item.titleKey)}
|
|
97
|
-
</h3>
|
|
98
|
-
<p className="text-text-secondary mb-4">
|
|
99
|
-
{t(item.descriptionKey)}
|
|
100
|
-
</p>
|
|
101
|
-
<Button type="primary" className="w-full">
|
|
102
|
-
{t(i18nKeys.HOME_EXPLORE)}
|
|
103
|
-
</Button>
|
|
104
|
-
</LocaleLink>
|
|
105
|
-
))} */}
|
|
106
|
-
</div>
|
|
107
|
-
</section>
|
|
108
|
-
|
|
109
82
|
{/* Call to Action Section */}
|
|
110
83
|
<section className="py-16 px-4 bg-elevated">
|
|
111
84
|
<div className="max-w-4xl mx-auto text-center">
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { ExecutorError } from '@qlover/fe-corekit';
|
|
2
2
|
import { NextResponse } from 'next/server';
|
|
3
|
-
import {
|
|
3
|
+
import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
4
4
|
import { AppErrorApi } from '@/server/AppErrorApi';
|
|
5
5
|
import { AppSuccessApi } from '@/server/AppSuccessApi';
|
|
6
6
|
import { AIService } from '@/server/services/AIService';
|
|
7
7
|
|
|
8
8
|
export async function GET() {
|
|
9
|
-
const
|
|
9
|
+
const result = await bootstrapServer.execNoError(
|
|
10
|
+
async ({ parameters: { IOC } }) => {
|
|
11
|
+
// const requestBody = await req.json();
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const result = await IOC(AIService).completions([
|
|
14
|
+
{
|
|
15
|
+
role: 'user',
|
|
16
|
+
content: 'hello'
|
|
17
|
+
}
|
|
18
|
+
]);
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
content: 'hello'
|
|
18
|
-
}
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
return result;
|
|
22
|
-
});
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
23
|
|
|
24
24
|
if (result instanceof ExecutorError) {
|
|
25
25
|
console.log(result);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import type { NextRequest } from 'next/server';
|
|
3
|
+
|
|
4
|
+
export function GET(req: NextRequest) {
|
|
5
|
+
return NextResponse.json({
|
|
6
|
+
message: 'Hello World',
|
|
7
|
+
hash: req.nextUrl.hash,
|
|
8
|
+
url: req.url,
|
|
9
|
+
href: req.nextUrl.href
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { createClient } from '@/lib/supabase/server';
|
|
3
|
+
|
|
4
|
+
export async function GET(request: NextRequest) {
|
|
5
|
+
const requestUrl = new URL(request.url);
|
|
6
|
+
const code = requestUrl.searchParams.get('code');
|
|
7
|
+
|
|
8
|
+
if (code) {
|
|
9
|
+
console.log('api/callback code', code);
|
|
10
|
+
try {
|
|
11
|
+
const supabase = await createClient();
|
|
12
|
+
// 使用 code 交换 session
|
|
13
|
+
const { error } = await supabase.auth.exchangeCodeForSession(code);
|
|
14
|
+
|
|
15
|
+
if (error) {
|
|
16
|
+
console.error('验证失败:', error.message);
|
|
17
|
+
// 重定向到错误页面
|
|
18
|
+
return NextResponse.redirect(
|
|
19
|
+
new URL('/auth/error?error=verification_failed', request.url)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 获取用户信息
|
|
24
|
+
const {
|
|
25
|
+
data: { user }
|
|
26
|
+
} = await supabase.auth.getUser();
|
|
27
|
+
|
|
28
|
+
// 检查邮箱是否已验证
|
|
29
|
+
if (user && user.email_confirmed_at) {
|
|
30
|
+
console.log('✅ 邮箱已验证成功');
|
|
31
|
+
// 重定向到成功页面
|
|
32
|
+
return NextResponse.redirect(new URL('/', request.url));
|
|
33
|
+
} else {
|
|
34
|
+
console.log('⏳ 邮箱验证中...');
|
|
35
|
+
// 重定向到验证提醒页面
|
|
36
|
+
return NextResponse.redirect(
|
|
37
|
+
new URL('/auth/verify-email', request.url)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('回调处理错误:', error);
|
|
42
|
+
return NextResponse.redirect(new URL('/auth/error', request.url));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 如果没有 code 参
|
|
47
|
+
// 数,重定向到登录页
|
|
48
|
+
return NextResponse.redirect(new URL('/auth/login', request.url));
|
|
49
|
+
}
|
|
@@ -35,4 +35,6 @@ export class AppConfig implements EnvConfigInterface {
|
|
|
35
35
|
|
|
36
36
|
public readonly openaiBaseUrl: string = process.env.CEREBRAS_BASE_URL!;
|
|
37
37
|
public readonly openaiApiKey: string = process.env.CEREBRAS_API_KEY!;
|
|
38
|
+
|
|
39
|
+
public readonly isProduction: boolean = this.env === 'production';
|
|
38
40
|
}
|
|
@@ -13,9 +13,9 @@ import type { I18nServiceInterface } from '../port/I18nServiceInterface';
|
|
|
13
13
|
import type { UIDialogInterface } from '@qlover/corekit-bridge';
|
|
14
14
|
|
|
15
15
|
@injectable()
|
|
16
|
-
export class DialogErrorPlugin
|
|
17
|
-
|
|
18
|
-
{
|
|
16
|
+
export class DialogErrorPlugin implements LifecyclePluginInterface<
|
|
17
|
+
ExecutorContextInterface<unknown>
|
|
18
|
+
> {
|
|
19
19
|
public readonly pluginName = 'DialogErrorPlugin';
|
|
20
20
|
|
|
21
21
|
constructor(
|
|
@@ -51,9 +51,6 @@ export class DialogErrorPlugin
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/**
|
|
55
|
-
* @override
|
|
56
|
-
*/
|
|
57
54
|
protected isI18nMessage(message: string): boolean {
|
|
58
55
|
return i18nKeySchema.safeParse(message).success;
|
|
59
56
|
}
|
|
@@ -6,8 +6,9 @@ import type {
|
|
|
6
6
|
EncryptorInterface
|
|
7
7
|
} from '@qlover/fe-corekit';
|
|
8
8
|
|
|
9
|
-
export interface RequestEncryptPluginProps<
|
|
10
|
-
|
|
9
|
+
export interface RequestEncryptPluginProps<
|
|
10
|
+
Request = unknown
|
|
11
|
+
> extends RequestAdapterConfig<Request> {
|
|
11
12
|
/**
|
|
12
13
|
* 加密密码在 HTTP 请求中
|
|
13
14
|
*
|
|
@@ -18,12 +19,9 @@ export interface RequestEncryptPluginProps<Request = unknown>
|
|
|
18
19
|
encryptProps?: string[] | string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export class RequestEncryptPlugin
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
ExecutorContextInterface<RequestEncryptPluginProps>
|
|
25
|
-
>
|
|
26
|
-
{
|
|
22
|
+
export class RequestEncryptPlugin implements LifecyclePluginInterface<
|
|
23
|
+
ExecutorContextInterface<RequestEncryptPluginProps>
|
|
24
|
+
> {
|
|
27
25
|
public readonly pluginName = 'RequestEncryptPlugin';
|
|
28
26
|
|
|
29
27
|
constructor(protected encryptor: EncryptorInterface<string, string>) {}
|
|
@@ -41,16 +39,16 @@ export class RequestEncryptPlugin
|
|
|
41
39
|
isObject(context.parameters.data) &&
|
|
42
40
|
encryptProps
|
|
43
41
|
) {
|
|
44
|
-
context.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
context.setParameters({
|
|
43
|
+
...context.parameters,
|
|
44
|
+
data: {
|
|
45
|
+
...context.parameters.data,
|
|
46
|
+
...this.encryptData(clone(context.parameters.data), encryptProps)
|
|
47
|
+
}
|
|
48
|
+
});
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
/**
|
|
52
|
-
* @override
|
|
53
|
-
*/
|
|
54
52
|
protected encryptData<T extends object>(
|
|
55
53
|
data: T,
|
|
56
54
|
encryptProps?: string | string[]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
|
+
import { ROUTE_LOGIN } from '@config/route';
|
|
2
3
|
import { NavigateBridge } from './NavigateBridge';
|
|
3
4
|
import type { RouterInterface, RouterPathname } from '../port/RouterInterface';
|
|
4
5
|
import type { UIBridgeInterface } from '@qlover/corekit-bridge';
|
|
@@ -27,16 +28,10 @@ export class RouterService implements RouterInterface {
|
|
|
27
28
|
this.goto('/');
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
/**
|
|
31
|
-
* @override
|
|
32
|
-
*/
|
|
33
31
|
public gotoLogin(): void {
|
|
34
|
-
this.goto(
|
|
32
|
+
this.goto(ROUTE_LOGIN);
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
/**
|
|
38
|
-
* @override
|
|
39
|
-
*/
|
|
40
35
|
public replaceHome(): void {
|
|
41
36
|
this.uiBridge.getUIBridge()?.replace('/');
|
|
42
37
|
}
|
|
@@ -18,9 +18,6 @@ export class StringEncryptor implements EncryptorInterface<string, string> {
|
|
|
18
18
|
this.key = appConfig.stringEncryptorKey;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
* @override
|
|
23
|
-
*/
|
|
24
21
|
protected encryptWithKey(str: string, key: string): string {
|
|
25
22
|
const result = [];
|
|
26
23
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -34,9 +31,6 @@ export class StringEncryptor implements EncryptorInterface<string, string> {
|
|
|
34
31
|
return result.join('');
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
/**
|
|
38
|
-
* @override
|
|
39
|
-
*/
|
|
40
34
|
protected decryptWithKey(str: string, key: string): string {
|
|
41
35
|
const result = [];
|
|
42
36
|
for (let i = 0; i < str.length; i++) {
|