@qlover/create-app 0.7.9 → 0.7.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/index.cjs +3 -3
  3. package/dist/index.js +3 -3
  4. package/dist/templates/next-app/.env.template +8 -4
  5. package/dist/templates/next-app/config/IOCIdentifier.ts +4 -1
  6. package/dist/templates/next-app/config/Identifier/api.ts +20 -0
  7. package/dist/templates/next-app/config/Identifier/index.ts +2 -0
  8. package/dist/templates/next-app/config/Identifier/page.login.ts +2 -2
  9. package/dist/templates/next-app/config/Identifier/page.register.ts +43 -22
  10. package/dist/templates/next-app/config/Identifier/validator.ts +34 -0
  11. package/dist/templates/next-app/config/i18n/index.ts +1 -0
  12. package/dist/templates/next-app/config/i18n/register18n.ts +44 -0
  13. package/dist/templates/next-app/migrations/schema/UserSchema.ts +13 -0
  14. package/dist/templates/next-app/migrations/sql/1694244000000.sql +10 -0
  15. package/dist/templates/next-app/package.json +11 -2
  16. package/dist/templates/next-app/public/locales/en.json +16 -2
  17. package/dist/templates/next-app/public/locales/zh.json +16 -2
  18. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +21 -0
  19. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +10 -0
  20. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +25 -5
  21. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +6 -4
  22. package/dist/templates/next-app/src/app/[locale]/page.tsx +3 -3
  23. package/dist/templates/next-app/src/app/[locale]/register/RegisterForm.tsx +176 -0
  24. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +79 -0
  25. package/dist/templates/next-app/src/app/api/user/login/route.ts +50 -0
  26. package/dist/templates/next-app/src/app/api/user/logout/route.ts +27 -0
  27. package/dist/templates/next-app/src/app/api/user/register/route.ts +50 -0
  28. package/dist/templates/next-app/src/base/cases/AppConfig.ts +19 -0
  29. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +35 -0
  30. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +70 -0
  31. package/dist/templates/next-app/src/base/cases/RouterService.ts +4 -0
  32. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +67 -0
  33. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +48 -0
  34. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +14 -0
  35. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +15 -0
  36. package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +18 -0
  37. package/dist/templates/next-app/src/base/port/DBMigrationInterface.ts +92 -0
  38. package/dist/templates/next-app/src/base/port/DBTableInterface.ts +3 -0
  39. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +3 -2
  40. package/dist/templates/next-app/src/base/port/MigrationApiInterface.ts +3 -0
  41. package/dist/templates/next-app/src/base/port/ServerApiResponseInterface.ts +6 -0
  42. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +3 -2
  43. package/dist/templates/next-app/src/base/services/I18nService.ts +9 -45
  44. package/dist/templates/next-app/src/base/services/UserService.ts +9 -8
  45. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +63 -0
  46. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +72 -0
  47. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +48 -0
  48. package/dist/templates/next-app/src/base/services/appApi/AppUserType.ts +51 -0
  49. package/dist/templates/next-app/src/base/services/migrations/MigrationsApi.ts +43 -0
  50. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +30 -5
  51. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +4 -3
  52. package/dist/templates/next-app/src/server/AppErrorApi.ts +10 -0
  53. package/dist/templates/next-app/src/server/AppSuccessApi.ts +7 -0
  54. package/dist/templates/next-app/src/server/PasswordEncrypt.ts +12 -0
  55. package/dist/templates/next-app/src/server/ServerAuth.ts +50 -0
  56. package/dist/templates/next-app/src/server/SupabaseBridge.ts +124 -0
  57. package/dist/templates/next-app/src/server/UserCredentialToken.ts +49 -0
  58. package/dist/templates/next-app/src/server/port/CrentialTokenInterface.ts +5 -0
  59. package/dist/templates/next-app/src/server/port/ServerInterface.ts +22 -0
  60. package/dist/templates/next-app/src/server/port/UserAuthInterface.ts +9 -0
  61. package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +15 -0
  62. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +8 -0
  63. package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +23 -0
  64. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +63 -0
  65. package/dist/templates/next-app/src/server/services/UserService.ts +105 -0
  66. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +79 -0
  67. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +1 -1
  68. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +8 -1
  69. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +20 -10
  70. package/dist/templates/next-app/tsconfig.json +3 -1
  71. package/package.json +2 -2
  72. package/dist/templates/next-app/src/base/cases/ServerAuth.ts +0 -17
  73. package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +0 -3
  74. package/dist/templates/next-app/src/base/port/ServerInterface.ts +0 -12
  75. /package/dist/templates/next-app/src/{app/[locale]/login → uikit/components}/FeatureItem.tsx +0 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @description 用户未找到
3
+ * @localZh 用户未找到
4
+ * @localEn User not found
5
+ */
6
+ export const API_USER_NOT_FOUND = 'api__user__not_found';
7
+
8
+ /**
9
+ * @description 用户已存在
10
+ * @localZh 用户已存在
11
+ * @localEn User already exists
12
+ */
13
+ export const API_USER_ALREADY_EXISTS = 'api__user__already_exists';
14
+
15
+ /**
16
+ * @description 响应不正确
17
+ * @localZh 响应不正确
18
+ * @localEn Response not correct
19
+ */
20
+ export const API_RESPONSE_NOT_OK = 'RESPONSE_NOT_OK';
@@ -1,3 +1,4 @@
1
+ export * from './api';
1
2
  export * from './common';
2
3
  export * from './common.error';
3
4
  export * from './page.about';
@@ -8,3 +9,4 @@ export * from './page.jsonStorage';
8
9
  export * from './page.login';
9
10
  export * from './page.register';
10
11
  export * from './page.request';
12
+ export * from './validator';
@@ -97,8 +97,8 @@ export const LOGIN_NO_ACCOUNT = 'login__no_account';
97
97
  export const LOGIN_CREATE_ACCOUNT = 'login__create_account';
98
98
  /**
99
99
  * @description Login page email validation message
100
- * @localZh 请输入您的邮箱!
101
- * @localEn Please input your email!
100
+ * @localZh 请输入正确的邮箱!
101
+ * @localEn Please input a valid email!
102
102
  */
103
103
  export const LOGIN_EMAIL_REQUIRED = 'login__email_required';
104
104
  /**
@@ -13,17 +13,56 @@ export const PAGE_REGISTER_TITLE = 'page__register__title';
13
13
  export const PAGE_REGISTER_DESCRIPTION = 'page__register__description';
14
14
 
15
15
  /**
16
- * @description Register page title
17
- * @localZh 创建账号
18
- * @localEn Create Account
16
+ * @description Register page content
17
+ * @localZh 创建账号页面内容
18
+ * @localEn Create Account Page Content
19
19
  */
20
- export const REGISTER_TITLE = 'register__title';
20
+ export const PAGE_REGISTER_CONTENT = 'page__register__content';
21
+
22
+ /**
23
+ * @description Register page keywords
24
+ * @localZh 创建账号页面关键词
25
+ * @localEn Create Account Page Keywords
26
+ */
27
+ export const PAGE_REGISTER_KEYWORDS = 'page__register__keywords';
28
+
29
+ /**
30
+ * @description Register page welcome
31
+ * @localZh 欢迎来到我们的平台
32
+ * @localEn Welcome to our platform
33
+ */
34
+ export const REGISTER_WELCOME = 'register__welcome';
35
+
21
36
  /**
22
37
  * @description Register page subtitle
23
38
  * @localZh 开始您的学习之旅
24
39
  * @localEn Start your learning journey
25
40
  */
26
41
  export const REGISTER_SUBTITLE = 'register__subtitle';
42
+
43
+ /**
44
+ * @description Register page feature ai paths
45
+ * @localZh AI路径
46
+ * @localEn AI Paths
47
+ */
48
+ export const REGISTER_FEATURE_AI_PATHS = 'register__feature__ai_paths';
49
+
50
+ /**
51
+ * @description Register page feature smart recommendations
52
+ * @localZh 智能推荐
53
+ * @localEn Smart Recommendations
54
+ */
55
+ export const REGISTER_FEATURE_SMART_RECOMMENDATIONS =
56
+ 'register__feature__smart_recommendations';
57
+
58
+ /**
59
+ * @description Register page feature progress tracking
60
+ * @localZh 进度跟踪
61
+ * @localEn Progress Tracking
62
+ */
63
+ export const REGISTER_FEATURE_PROGRESS_TRACKING =
64
+ 'register__feature__progress_tracking';
65
+
27
66
  /**
28
67
  * @description Register page username field
29
68
  * @localZh 用户名
@@ -121,24 +160,6 @@ export const REGISTER_HAVE_ACCOUNT = 'register__have_account';
121
160
  * @localEn Sign in
122
161
  */
123
162
  export const REGISTER_LOGIN_LINK = 'register__login_link';
124
- /**
125
- * @description Register page feature item - Personalized Learning
126
- * @localZh 个性化学习体验
127
- * @localEn Personalized Learning Experience
128
- */
129
- export const REGISTER_FEATURE_PERSONALIZED = 'register__feature__personalized';
130
- /**
131
- * @description Register page feature item - Expert Support
132
- * @localZh 专家支持和指导
133
- * @localEn Expert Support and Guidance
134
- */
135
- export const REGISTER_FEATURE_SUPPORT = 'register__feature__support';
136
- /**
137
- * @description Register page feature item - Learning Community
138
- * @localZh 活跃的学习社区
139
- * @localEn Active Learning Community
140
- */
141
- export const REGISTER_FEATURE_COMMUNITY = 'register__feature__community';
142
163
  /**
143
164
  * @description Register page terms agreement required message
144
165
  * @localZh 请同意服务条款和隐私政策
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @description Validator for login params
3
+ * @localZh 不是一个有效的登录参数
4
+ * @localEn Not a valid login parameter
5
+ */
6
+ export const V_LOGIN_PARAMS_REQUIRED = 'v_login_params_required';
7
+
8
+ /**
9
+ * @description Invalid email format validation message
10
+ * @localZh 邮箱格式无效的验证消息
11
+ * @localEn Invalid email format validation message
12
+ */
13
+ export const V_EMAIL_INVALID = 'validator_email_invalid';
14
+
15
+ /**
16
+ * @description Password minimum length validation message
17
+ * @localZh 密码最小长度验证消息(6)
18
+ * @localEn Password minimum length validation message(6)
19
+ */
20
+ export const V_PASSWORD_MIN_LENGTH = 'validator_password_min_length';
21
+
22
+ /**
23
+ * @description Password maximum length validation message
24
+ * @localZh 密码最大长度验证消息(50)
25
+ * @localEn Password maximum length validation message(50)
26
+ */
27
+ export const V_PASSWORD_MAX_LENGTH = 'validator_password_max_length';
28
+
29
+ /**
30
+ * @description Password whitespace validation message
31
+ * @localZh 密码不能包含空格的验证消息
32
+ * @localEn Password cannot contain whitespace characters validation message
33
+ */
34
+ export const V_PASSWORD_SPECIAL_CHARS = 'validator_password_special_chars';
@@ -1,3 +1,4 @@
1
1
  export * from './i18nConfig';
2
2
  export * from './PageI18nInterface';
3
3
  export * from './loginI18n';
4
+ export * from './register18n';
@@ -0,0 +1,44 @@
1
+ import * as i18nKeys from '../Identifier/page.register';
2
+
3
+ /**
4
+ * Register page i18n interface
5
+ *
6
+ * @description
7
+ * - welcome: welcome message
8
+ */
9
+ export type RegisterI18nInterface = typeof register18n;
10
+
11
+ export const register18n = Object.freeze({
12
+ // basic meta properties
13
+ title: i18nKeys.PAGE_REGISTER_TITLE,
14
+ description: i18nKeys.PAGE_REGISTER_DESCRIPTION,
15
+ content: i18nKeys.PAGE_REGISTER_CONTENT,
16
+ keywords: i18nKeys.PAGE_REGISTER_KEYWORDS,
17
+
18
+ // register page
19
+ welcome: i18nKeys.REGISTER_WELCOME,
20
+ subtitle: i18nKeys.REGISTER_SUBTITLE,
21
+ feature_ai_paths: i18nKeys.REGISTER_FEATURE_AI_PATHS,
22
+ feature_smart_recommendations:
23
+ i18nKeys.REGISTER_FEATURE_SMART_RECOMMENDATIONS,
24
+ feature_progress_tracking: i18nKeys.REGISTER_FEATURE_PROGRESS_TRACKING,
25
+
26
+ // register form
27
+ username: i18nKeys.REGISTER_USERNAME,
28
+ username_required: i18nKeys.REGISTER_USERNAME_REQUIRED,
29
+ email: i18nKeys.REGISTER_EMAIL,
30
+ email_required: i18nKeys.REGISTER_EMAIL_REQUIRED,
31
+ password: i18nKeys.REGISTER_PASSWORD,
32
+ password_required: i18nKeys.REGISTER_PASSWORD_REQUIRED,
33
+ confirm_password_required: i18nKeys.REGISTER_CONFIRM_PASSWORD_REQUIRED,
34
+ password_mismatch: i18nKeys.REGISTER_PASSWORD_MISMATCH,
35
+ button: i18nKeys.REGISTER_BUTTON,
36
+ terms_prefix: i18nKeys.REGISTER_TERMS_PREFIX,
37
+ terms_link: i18nKeys.REGISTER_TERMS_LINK,
38
+ terms_and: i18nKeys.REGISTER_TERMS_AND,
39
+ privacy_link: i18nKeys.REGISTER_PRIVACY_LINK,
40
+ have_account: i18nKeys.REGISTER_HAVE_ACCOUNT,
41
+ confirm_password: i18nKeys.REGISTER_CONFIRM_PASSWORD,
42
+ terms_required: i18nKeys.REGISTER_TERMS_REQUIRED,
43
+ login_link: i18nKeys.REGISTER_LOGIN_LINK
44
+ });
@@ -0,0 +1,13 @@
1
+ export interface UserSchema {
2
+ id: number;
3
+ role: string;
4
+ email: string;
5
+ password: string;
6
+ /**
7
+ * 加密的token, 包含token, 过期时间
8
+ */
9
+ credential_token: string;
10
+ email_confirmed_at: number;
11
+ created_at?: number;
12
+ updated_at?: number;
13
+ }
@@ -0,0 +1,10 @@
1
+ CREATE TABLE IF NOT EXISTS fe_users (
2
+ id BIGSERIAL PRIMARY KEY,
3
+ role TEXT NOT NULL,
4
+ email TEXT NOT NULL,
5
+ password TEXT NOT NULL,
6
+ credential_token TEXT,
7
+ email_confirmed_at TIMESTAMPTZ,
8
+ created_at TIMESTAMPTZ DEFAULT NOW(),
9
+ updated_at TIMESTAMPTZ DEFAULT NOW()
10
+ );
@@ -25,26 +25,34 @@
25
25
  "@qlover/corekit-bridge": "^1.6.4",
26
26
  "@qlover/fe-corekit": "^2.1.0",
27
27
  "@qlover/slice-store-react": "^1.4.1",
28
+ "@supabase/auth-helpers-nextjs": "^0.10.0",
29
+ "@supabase/ssr": "^0.7.0",
30
+ "@supabase/supabase-js": "^2.57.2",
31
+ "@types/jsonwebtoken": "^9.0.10",
28
32
  "antd": "^5.27.1",
29
33
  "clsx": "^2.1.1",
30
34
  "inversify": "^7.8.1",
35
+ "jsonwebtoken": "^9.0.2",
31
36
  "lodash": "^4.17.21",
32
37
  "next": "15.5.0",
33
38
  "next-intl": "^4.3.5",
34
39
  "next-themes": "^0.4.6",
35
40
  "react": "19.1.0",
36
- "react-dom": "19.1.0"
41
+ "react-dom": "19.1.0",
42
+ "zod": "^4.1.8"
37
43
  },
38
44
  "devDependencies": {
39
45
  "@brain-toolkit/ts2locales": "^0.2.3",
40
46
  "@eslint/eslintrc": "^3",
47
+ "@qlover/env-loader": "^0.3.0",
48
+ "@qlover/eslint-plugin": "latest",
41
49
  "@tailwindcss/postcss": "^4",
42
50
  "@types/lodash": "^4.17.20",
43
51
  "@types/node": "^20",
44
52
  "@types/react": "^19",
45
53
  "@types/react-dom": "^19",
46
- "@qlover/eslint-plugin": "latest",
47
54
  "cross-env": "^7.0.3",
55
+ "dotenv-expand": "^12.0.3",
48
56
  "eslint": "^9",
49
57
  "eslint-config-next": "15.5.0",
50
58
  "eslint-config-prettier": "^10.1.8",
@@ -52,6 +60,7 @@
52
60
  "eslint-plugin-prettier": "^5.5.4",
53
61
  "eslint-plugin-unused-imports": "^4.2.0",
54
62
  "prettier": "^3.6.2",
63
+ "supabase": "^2.40.7",
55
64
  "tailwindcss": "^4",
56
65
  "typescript": "^5"
57
66
  }
@@ -114,7 +114,7 @@
114
114
  "login__with_google": "Sign in with Google",
115
115
  "login__no_account": "Don't have an account?",
116
116
  "login__create_account": "Create one here",
117
- "login__email_required": "Please input your email!",
117
+ "login__email_required": "Please input a valid email!",
118
118
  "login__password_required": "Please input your password!",
119
119
  "login__feature__ai_paths": "AI-powered personalized learning paths",
120
120
  "login__feature__smart_recommendations": "Smart content recommendations",
@@ -181,5 +181,19 @@
181
181
  "page__login__content": "Login Page Content",
182
182
  "page__login__keywords": "Login Page Keywords",
183
183
  "err__server__auth__error": "Server auth error",
184
- "page__home__keywords": "Modern frontend utility library, practical tools, components"
184
+ "page__home__keywords": "Modern frontend utility library, practical tools, components",
185
+ "api__user__not_found": "User not found",
186
+ "api__user__already_exists": "User already exists",
187
+ "RESPONSE_NOT_OK": "Response not correct",
188
+ "v_login_params_required": "Not a valid login parameter",
189
+ "validator_email_invalid": "Invalid email format validation message",
190
+ "validator_password_min_length": "Password minimum length validation message(6)",
191
+ "validator_password_max_length": "Password maximum length validation message(50)",
192
+ "validator_password_special_chars": "Password cannot contain whitespace characters validation message",
193
+ "page__register__content": "Create Account Page Content",
194
+ "page__register__keywords": "Create Account Page Keywords",
195
+ "register__welcome": "Welcome to our platform",
196
+ "register__feature__ai_paths": "AI Paths",
197
+ "register__feature__smart_recommendations": "Smart Recommendations",
198
+ "register__feature__progress_tracking": "Progress Tracking"
185
199
  }
@@ -114,7 +114,7 @@
114
114
  "login__with_google": "使用 Google 登录",
115
115
  "login__no_account": "还没有账号?",
116
116
  "login__create_account": "在此创建",
117
- "login__email_required": "请输入您的邮箱!",
117
+ "login__email_required": "请输入正确的邮箱!",
118
118
  "login__password_required": "请输入您的密码!",
119
119
  "login__feature__ai_paths": "AI驱动的个性化学习路径",
120
120
  "login__feature__smart_recommendations": "智能内容推荐",
@@ -181,5 +181,19 @@
181
181
  "page__login__content": "登录页面内容",
182
182
  "page__login__keywords": "登录页面关键词",
183
183
  "err__server__auth__error": "服务器认证错误",
184
- "page__home__keywords": "现代前端实用库, 实用工具, 组件"
184
+ "page__home__keywords": "现代前端实用库, 实用工具, 组件",
185
+ "api__user__not_found": "用户未找到",
186
+ "api__user__already_exists": "用户已存在",
187
+ "RESPONSE_NOT_OK": "响应不正确",
188
+ "v_login_params_required": "不是一个有效的登录参数",
189
+ "validator_email_invalid": "邮箱格式无效的验证消息",
190
+ "validator_password_min_length": "密码最小长度验证消息(6)",
191
+ "validator_password_max_length": "密码最大长度验证消息(50)",
192
+ "validator_password_special_chars": "密码不能包含空格的验证消息",
193
+ "page__register__content": "创建账号页面内容",
194
+ "page__register__keywords": "创建账号页面关键词",
195
+ "register__welcome": "欢迎来到我们的平台",
196
+ "register__feature__ai_paths": "AI路径",
197
+ "register__feature__smart_recommendations": "智能推荐",
198
+ "register__feature__progress_tracking": "进度跟踪"
185
199
  }
@@ -0,0 +1,21 @@
1
+ import { PageParams } from '@/base/cases/PageParams';
2
+ import type { PageLayoutProps } from '@/base/types/PageProps';
3
+ import '@/styles/css/index.css';
4
+
5
+ export default async function AdminLayout({
6
+ children,
7
+ params
8
+ }: PageLayoutProps) {
9
+ const pageParams = new PageParams(await params!);
10
+ const locale = pageParams.getLocale();
11
+
12
+ return (
13
+ <div
14
+ data-testid="AdminRootLayout"
15
+ lang={locale}
16
+ className="min-h-screen bg-primary text-text"
17
+ >
18
+ {children}
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+ import Link from 'next/link';
3
+
4
+ export default function AdminPage() {
5
+ return (
6
+ <div data-testid="AdminPage">
7
+ <Link href="/admin/migrations">Migrations</Link>
8
+ </div>
9
+ );
10
+ }
@@ -2,8 +2,10 @@
2
2
 
3
3
  import { UserOutlined, LockOutlined, GoogleOutlined } from '@ant-design/icons';
4
4
  import { Form, Input, Button } from 'antd';
5
+ import { useTranslations } from 'next-intl';
5
6
  import { useState } from 'react';
6
7
  import { I } from '@config/IOCIdentifier';
8
+ import { LoginValidator } from '@/server/validators/LoginValidator';
7
9
  import { LocaleLink } from '@/uikit/components/LocaleLink';
8
10
  import { useIOC } from '@/uikit/hook/useIOC';
9
11
  import type { LoginI18nInterface } from '@config/i18n/loginI18n';
@@ -15,7 +17,10 @@ interface LoginFormData {
15
17
 
16
18
  export function LoginForm(props: { tt: LoginI18nInterface }) {
17
19
  const { tt } = props;
20
+ const t = useTranslations();
18
21
  const userService = useIOC(I.UserServiceInterface);
22
+ const logger = useIOC(I.Logger);
23
+ const appConfig = useIOC(I.AppConfig);
19
24
  const routerService = useIOC(I.RouterServiceInterface);
20
25
  const [loading, setLoading] = useState(false);
21
26
 
@@ -25,7 +30,7 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
25
30
  await userService.login(values);
26
31
  routerService.gotoHome();
27
32
  } catch (error) {
28
- console.error('Login error:', error);
33
+ logger.error(error);
29
34
  } finally {
30
35
  setLoading(false);
31
36
  }
@@ -38,30 +43,45 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
38
43
  onFinish={handleLogin}
39
44
  layout="vertical"
40
45
  className="space-y-4"
46
+ validateTrigger="onSubmit"
47
+ initialValues={{
48
+ email: appConfig.testLoginEmail,
49
+ password: appConfig.testLoginPassword
50
+ }}
41
51
  >
42
52
  <Form.Item
43
53
  name="email"
44
- rules={[{ required: true, message: tt.emailRequired }]}
54
+ rules={[{ required: true, type: 'email', message: tt.emailRequired }]}
45
55
  >
46
56
  <Input
47
57
  prefix={<UserOutlined className="text-text-tertiary" />}
48
58
  placeholder={tt.email}
49
59
  title={tt.emailTitle}
50
60
  className="h-12 text-base bg-secondary border-c-border"
51
- autoComplete="off"
52
61
  />
53
62
  </Form.Item>
54
63
 
55
64
  <Form.Item
56
65
  name="password"
57
- rules={[{ required: true, message: tt.passwordRequired }]}
66
+ rules={[
67
+ { required: true, message: tt.passwordRequired },
68
+ {
69
+ validator(_, value) {
70
+ const validator = new LoginValidator();
71
+ const result = validator.validatePassword(value);
72
+ if (result != null) {
73
+ return Promise.reject(t(result.message));
74
+ }
75
+ return Promise.resolve();
76
+ }
77
+ }
78
+ ]}
58
79
  >
59
80
  <Input.Password
60
81
  prefix={<LockOutlined />}
61
82
  placeholder={tt.password}
62
83
  title={tt.passwordTitle}
63
84
  className="h-12 text-base"
64
- autoComplete="new-password"
65
85
  />
66
86
  </Form.Item>
67
87
 
@@ -1,12 +1,12 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import { loginI18n, i18nConfig } from '@config/i18n';
3
3
  import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
4
- import { ServerAuth } from '@/base/cases/ServerAuth';
5
4
  import type { PageParamsProps } from '@/base/types/PageProps';
6
5
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
7
6
  import { redirect } from '@/i18n/routing';
7
+ import { ServerAuth } from '@/server/ServerAuth';
8
8
  import { BaseLayout } from '@/uikit/components/BaseLayout';
9
- import { FeatureItem } from './FeatureItem';
9
+ import { FeatureItem } from '@/uikit/components/FeatureItem';
10
10
  import { LoginForm } from './LoginForm';
11
11
  import type { Metadata } from 'next';
12
12
 
@@ -43,7 +43,7 @@ export default async function LoginPage(props: PageParamsProps) {
43
43
 
44
44
  const server = new BootstrapServer();
45
45
 
46
- if (await new ServerAuth(server).hasAuth()) {
46
+ if (await server.getIOC(ServerAuth).hasAuth()) {
47
47
  return redirect({ href: '/', locale: params.locale! });
48
48
  }
49
49
 
@@ -52,7 +52,9 @@ export default async function LoginPage(props: PageParamsProps) {
52
52
  return (
53
53
  <BaseLayout
54
54
  data-testid="LoginPage"
55
- className="text-xs1 bg-primary flex flex-col min-h-screen"
55
+ mainProps={{
56
+ className: 'text-xs1 bg-primary flex min-h-screen'
57
+ }}
56
58
  >
57
59
  <div className="hidden lg:flex bg-secondary lg:w-1/2 p-12 flex-col">
58
60
  <h1 className="text-4xl font-bold text-text mb-4">{tt.welcome}</h1>
@@ -2,10 +2,10 @@ import { Button } from 'antd';
2
2
  import { i18nConfig } from '@config/i18n';
3
3
  import { homeI18n } from '@config/i18n/HomeI18n ';
4
4
  import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
5
- import { ServerAuth } from '@/base/cases/ServerAuth';
6
5
  import type { PageParamsProps } from '@/base/types/PageProps';
7
6
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
8
7
  import { redirect } from '@/i18n/routing';
8
+ import { ServerAuth } from '@/server/ServerAuth';
9
9
  import { BaseLayout } from '@/uikit/components/BaseLayout';
10
10
  import type { Metadata } from 'next';
11
11
 
@@ -45,12 +45,12 @@ export default async function Home({ params }: PageParamsProps) {
45
45
  const locale = pageParams.getLocale();
46
46
  const tt = await pageParams.getI18nInterface(homeI18n);
47
47
 
48
- if (!(await new ServerAuth(server).hasAuth())) {
48
+ if (!(await server.getIOC(ServerAuth).hasAuth())) {
49
49
  return redirect({ href: '/login', locale });
50
50
  }
51
51
 
52
52
  return (
53
- <BaseLayout data-testid="HomePage">
53
+ <BaseLayout data-testid="HomePage" showLogoutButton>
54
54
  {/* Hero Section */}
55
55
  <section className="py-16 px-4">
56
56
  <div className="max-w-4xl mx-auto text-center">