@qlover/create-app 1.0.1 → 1.0.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/config/Identifier/api.ts +7 -0
  5. package/dist/templates/next-app/config/Identifier/pages/page.register.ts +8 -0
  6. package/dist/templates/next-app/config/common.ts +1 -1
  7. package/dist/templates/next-app/config/i18n/HomeI18n.ts +2 -0
  8. package/dist/templates/next-app/config/i18n/register18n.ts +2 -1
  9. package/dist/templates/next-app/config/route.ts +9 -0
  10. package/dist/templates/next-app/migrations/schema/UserSchema.ts +1 -1
  11. package/dist/templates/next-app/next.config.ts +5 -4
  12. package/dist/templates/next-app/package.json +7 -8
  13. package/dist/templates/next-app/public/locales/en.json +4 -1
  14. package/dist/templates/next-app/public/locales/zh.json +4 -1
  15. package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +18 -0
  16. package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/LoginForm.tsx +2 -1
  17. package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/page.tsx +4 -5
  18. package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +8 -0
  19. package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/RegisterForm.tsx +24 -3
  20. package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/page.tsx +4 -5
  21. package/dist/templates/next-app/src/app/[locale]/page.tsx +18 -45
  22. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +13 -13
  23. package/dist/templates/next-app/src/app/api/auth/callback/route.ts +11 -0
  24. package/dist/templates/next-app/src/app/api/callback/route.ts +49 -0
  25. package/dist/templates/next-app/src/base/cases/AppConfig.ts +2 -0
  26. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +3 -6
  27. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -1
  28. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +13 -15
  29. package/dist/templates/next-app/src/base/cases/RouterService.ts +2 -7
  30. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -6
  31. package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +53 -0
  32. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -10
  33. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -3
  34. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +1 -1
  35. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -18
  36. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +10 -5
  37. package/dist/templates/next-app/src/base/services/{appApi/AppApiRequester.ts → AppApiRequester.ts} +16 -11
  38. package/dist/templates/next-app/src/base/services/AppUserGateway.ts +110 -0
  39. package/dist/templates/next-app/src/base/services/I18nService.ts +1 -7
  40. package/dist/templates/next-app/src/base/services/ResourceService.ts +1 -4
  41. package/dist/templates/next-app/src/base/services/UserService.ts +28 -17
  42. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +5 -7
  43. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +4 -3
  44. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +24 -16
  45. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +2 -5
  46. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +15 -18
  47. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -5
  48. package/dist/templates/next-app/src/core/globals.ts +1 -0
  49. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +2 -8
  50. package/dist/templates/next-app/src/i18n/routing.ts +8 -3
  51. package/dist/templates/next-app/src/lib/supabase/client.ts +8 -0
  52. package/dist/templates/next-app/src/lib/supabase/conts.ts +2 -0
  53. package/dist/templates/next-app/src/lib/supabase/proxy.ts +84 -0
  54. package/dist/templates/next-app/src/lib/supabase/server.ts +38 -0
  55. package/dist/templates/next-app/src/proxy.ts +8 -1
  56. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +5 -2
  57. package/dist/templates/next-app/src/server/NextApiServer.ts +2 -9
  58. package/dist/templates/next-app/src/server/PagesRouteParams.ts +3 -4
  59. package/dist/templates/next-app/src/server/ServerAuth.ts +18 -12
  60. package/dist/templates/next-app/src/server/SupabaseBridge.ts +66 -59
  61. package/dist/templates/next-app/src/server/controllers/UserController.ts +7 -2
  62. package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +4 -0
  63. package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -1
  64. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +7 -1
  65. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -3
  66. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -3
  67. package/dist/templates/next-app/src/server/services/UserService.ts +71 -51
  68. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -3
  69. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -6
  70. package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +68 -0
  71. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +3 -3
  72. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -6
  73. package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +29 -0
  74. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +21 -28
  75. package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +20 -0
  76. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +2 -2
  77. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +3 -17
  78. package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +8 -0
  79. package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +42 -0
  80. package/package.json +1 -1
  81. package/dist/templates/next-app/src/base/cases/TranslateI18nInterface.ts +0 -25
  82. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +0 -78
  83. package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +0 -25
  84. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +0 -78
  85. package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +0 -8
@@ -1,10 +1,10 @@
1
- import { TeamOutlined } from '@ant-design/icons';
2
1
  import { clsx } from 'clsx';
3
2
  import { useLocale } from 'next-intl';
4
- import { useMemo, type HTMLAttributes } from 'react';
3
+ import { Suspense, type HTMLAttributes } from 'react';
4
+ import { AdminButton } from './AdminButton';
5
5
  import { AppBridge } from './AppBridge';
6
+ import { AuthButton } from './AuthButton';
6
7
  import { LanguageSwitcher } from './LanguageSwitcher';
7
- import { LogoutButton } from './LogoutButton';
8
8
  import { ThemeSwitcher } from './ThemeSwitcher';
9
9
  import { LocaleLink } from '../components/LocaleLink';
10
10
 
@@ -14,9 +14,9 @@ export interface AppRoutePageTT {
14
14
  }
15
15
 
16
16
  export interface AppRoutePageProps extends HTMLAttributes<HTMLDivElement> {
17
- showLogoutButton?: boolean;
18
17
  showAdminButton?: boolean;
19
18
  mainProps?: HTMLAttributes<HTMLElement>;
19
+ showAuthButton?: boolean;
20
20
  headerClassName?: string;
21
21
  headerHref?: string;
22
22
  tt: AppRoutePageTT;
@@ -34,10 +34,10 @@ export interface AppRoutePageProps extends HTMLAttributes<HTMLDivElement> {
34
34
  */
35
35
  export function AppRoutePage({
36
36
  children,
37
- showLogoutButton,
38
37
  showAdminButton,
39
38
  mainProps,
40
39
  headerClassName,
40
+ showAuthButton,
41
41
  tt,
42
42
  headerHref = '/',
43
43
  ...props
@@ -45,28 +45,6 @@ export function AppRoutePage({
45
45
  const locale = useLocale();
46
46
  const adminTitle = tt.adminTitle;
47
47
 
48
- const actions = useMemo(() => {
49
- return [
50
- showAdminButton && (
51
- <LocaleLink
52
- key="admin-button"
53
- href="/admin"
54
- title={adminTitle}
55
- locale={locale}
56
- className="text-text hover:text-text-hover cursor-pointer text-lg transition-colors"
57
- >
58
- <TeamOutlined className="text-lg text-text" />
59
- </LocaleLink>
60
- ),
61
-
62
- <LanguageSwitcher key="language-switcher" />,
63
-
64
- <ThemeSwitcher key="theme-switcher" />,
65
-
66
- showLogoutButton && <LogoutButton key="logout-button" />
67
- ].filter(Boolean);
68
- }, [adminTitle, showAdminButton, showLogoutButton, locale]);
69
-
70
48
  return (
71
49
  <div
72
50
  data-testid="AppRoutePage"
@@ -100,7 +78,22 @@ export function AppRoutePage({
100
78
  </span>
101
79
  </LocaleLink>
102
80
  </div>
103
- <div className="flex items-center gap-2 md:gap-4">{actions}</div>
81
+ <div className="flex items-center gap-2 md:gap-4">
82
+ {showAdminButton && (
83
+ <Suspense>
84
+ <AdminButton adminTitle={adminTitle} locale={locale} />
85
+ </Suspense>
86
+ )}
87
+
88
+ <LanguageSwitcher key="language-switcher" />
89
+ <ThemeSwitcher key="theme-switcher" />
90
+
91
+ {showAuthButton && (
92
+ <Suspense>
93
+ <AuthButton />
94
+ </Suspense>
95
+ )}
96
+ </div>
104
97
  </div>
105
98
  </header>
106
99
 
@@ -0,0 +1,20 @@
1
+ import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
2
+ import { Link } from '@/i18n/routing';
3
+ import { ServerAuth } from '@/server/ServerAuth';
4
+ import { ROUTE_LOGIN, ROUTE_REGISTER } from '@config/route';
5
+ import { LogoutButton } from './LogoutButton';
6
+
7
+ export async function AuthButton() {
8
+ const hasAuth = await bootstrapServer.getIOC(ServerAuth).hasAuth();
9
+
10
+ if (hasAuth) {
11
+ return <LogoutButton data-testid="logout-button" />;
12
+ }
13
+
14
+ return (
15
+ <div data-testid="AuthButton" className="flex gap-2" data-auth={hasAuth}>
16
+ <Link href={ROUTE_LOGIN}>Sign in</Link>
17
+ <Link href={ROUTE_REGISTER}>Sign up</Link>
18
+ </div>
19
+ );
20
+ }
@@ -1,5 +1,5 @@
1
1
  import { useMemo } from 'react';
2
- import { TranslateI18nInterface } from '@/base/cases/TranslateI18nInterface';
2
+ import { TranslateI18nUtil } from '@/base/cases/TranslateI18nUtil';
3
3
  import { useWarnTranslations } from './useWarnTranslations';
4
4
 
5
5
  /**
@@ -14,7 +14,7 @@ export function useI18nInterface<T extends Record<string, string>>(
14
14
  const t = useWarnTranslations();
15
15
 
16
16
  const i18n = useMemo(
17
- () => TranslateI18nInterface.translate(i18nInterface, t),
17
+ () => TranslateI18nUtil.translate(i18nInterface, t),
18
18
  [i18nInterface, t]
19
19
  );
20
20
 
@@ -1,25 +1,11 @@
1
1
  import { useTranslations as useNextTranslations } from 'next-intl';
2
- import { useCallback } from 'react';
3
- import { i18nWarnMissingTranslation } from '@config/common';
2
+ import { useMemo } from 'react';
3
+ import { TranslateI18nUtil } from '@/base/cases/TranslateI18nUtil';
4
4
 
5
5
  export function useWarnTranslations() {
6
6
  const t = useNextTranslations();
7
7
 
8
- const overrideT = useCallback(
9
- (key: string) => {
10
- if (!i18nWarnMissingTranslation) {
11
- return t(key);
12
- }
13
-
14
- if (t.has(key)) {
15
- return t(key);
16
- }
17
-
18
- console.warn(`[i18n] Missing translation: ${key}`);
19
- return key;
20
- },
21
- [t]
22
- );
8
+ const overrideT = useMemo(() => TranslateI18nUtil.overrideTranslateT(t), [t]);
23
9
 
24
10
  return Object.assign(overrideT, t);
25
11
  }
@@ -0,0 +1,8 @@
1
+ export function getHashParams(hash: string): Record<string, string | null> {
2
+ const sp = new URLSearchParams(hash.startsWith('#') ? hash.slice(1) : hash);
3
+ const params = {};
4
+ sp.forEach((value, key) => {
5
+ Object.assign(params, { [key]: value });
6
+ });
7
+ return params;
8
+ }
@@ -0,0 +1,42 @@
1
+ import { isString, pick } from 'lodash';
2
+ import type { SignupVerifyParamType } from '@/server/validators/SignupVerifyValidator';
3
+
4
+ export const emailVerifyParamKeys = [
5
+ 'access_token',
6
+ 'expires_at',
7
+ 'expires_in',
8
+ 'refresh_token',
9
+ 'token_type',
10
+ 'type'
11
+ ] as const;
12
+
13
+ export function getHashParams(hash: string): Record<string, string | null> {
14
+ const sp = new URLSearchParams(hash.startsWith('#') ? hash.slice(1) : hash);
15
+ const params = {};
16
+ sp.forEach((value, key) => {
17
+ Object.assign(params, { [key]: value });
18
+ });
19
+ return params;
20
+ }
21
+
22
+ export function isEmailVerifyParam(
23
+ value: unknown
24
+ ): value is SignupVerifyParamType {
25
+ return emailVerifyParamKeys.every(
26
+ (key) =>
27
+ emailVerifyParamKeys.includes(key) &&
28
+ (value as SignupVerifyParamType)[key] != null &&
29
+ isString((value as SignupVerifyParamType)[key])
30
+ );
31
+ }
32
+
33
+ export function getHashVerifyEmailParams(
34
+ hash: string
35
+ ): SignupVerifyParamType | null {
36
+ if (!hash) {
37
+ return null;
38
+ }
39
+
40
+ const hashParams = pick(getHashParams(hash), emailVerifyParamKeys);
41
+ return isEmailVerifyParam(hashParams) ? hashParams : null;
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlover/create-app",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Create a new app with a single command",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1,25 +0,0 @@
1
- import type { PageI18nInterface } from '@config/i18n';
2
- import type { useTranslations } from 'next-intl';
3
-
4
- /**
5
- * Translate I18n Interface tools class
6
- *
7
- * @param i18nInterface - The i18n interface to translate
8
- * @param t - The translations function
9
- * @returns The translated i18n interface
10
- */
11
- export class TranslateI18nInterface {
12
- public static translate<T extends PageI18nInterface | Record<string, string>>(
13
- source: T,
14
- t: ReturnType<typeof useTranslations>
15
- ): T {
16
- return Object.fromEntries(
17
- Object.entries(source).map(([key, value]) => {
18
- if (typeof value === 'string') {
19
- return [key, t(value)];
20
- }
21
- return [key, value];
22
- })
23
- ) as T;
24
- }
25
- }
@@ -1,78 +0,0 @@
1
- import { inject, injectable } from 'inversify';
2
- import { omit } from 'lodash';
3
- import type { LoginValidatorData } from '@/server/validators/LoginValidator';
4
- import {
5
- isWebUserSchema,
6
- type UserCredential,
7
- type UserSchema
8
- } from '@migrations/schema/UserSchema';
9
- import { AppUserApi } from '../services/appApi/AppUserApi';
10
- import type { AppUserApiInterface } from '../port/AppUserApiInterface';
11
- import type { UserServiceGateway } from '@qlover/corekit-bridge';
12
-
13
- @injectable()
14
- export class UserServiceApi implements UserServiceGateway<
15
- UserSchema,
16
- UserCredential
17
- > {
18
- constructor(@inject(AppUserApi) protected appUserApi: AppUserApiInterface) {}
19
-
20
- /**
21
- * @override
22
- */
23
- public getUserInfo(_params?: unknown): Promise<UserSchema | null> {
24
- if (_params && isWebUserSchema(_params).success) {
25
- return Promise.resolve(omit(_params, 'credential_token') as UserSchema);
26
- }
27
-
28
- return Promise.resolve(null);
29
- }
30
-
31
- /**
32
- * @override
33
- */
34
- public refreshUserInfo<Params>(
35
- _params?: Params | undefined
36
- ): Promise<UserSchema | null> {
37
- return this.getUserInfo(_params);
38
- }
39
-
40
- /**
41
- * @override
42
- */
43
- public async login(params: LoginValidatorData): Promise<UserCredential> {
44
- const response = await this.appUserApi.login(params);
45
-
46
- if (!response.data.success) {
47
- throw response;
48
- }
49
-
50
- return response.data.data as UserCredential;
51
- }
52
-
53
- /**
54
- * @override
55
- */
56
- public async register(params: LoginValidatorData): Promise<UserSchema> {
57
- const response = await this.appUserApi.register(params);
58
-
59
- if (!response.data.success) {
60
- throw response;
61
- }
62
-
63
- return response.data.data as UserSchema;
64
- }
65
-
66
- /**
67
- * @override
68
- */
69
- public async logout<P = unknown, Result = void>(params?: P): Promise<Result> {
70
- const response = await this.appUserApi.logout(params);
71
-
72
- if (!response.data.success) {
73
- throw response;
74
- }
75
-
76
- return response.data.data as Result;
77
- }
78
- }
@@ -1,25 +0,0 @@
1
- import {
2
- ExecutorContextInterface,
3
- RequestAdapterFetch,
4
- RequestExecutor
5
- } from '@qlover/fe-corekit';
6
- import { injectable } from 'inversify';
7
- import type { AppApiConfig } from '../appApi/AppApiRequester';
8
-
9
- export interface AdminApiRequesterContext
10
- extends ExecutorContextInterface<AppApiConfig> {}
11
-
12
- @injectable()
13
- export class AdminApiRequester extends RequestExecutor<
14
- AppApiConfig,
15
- AdminApiRequesterContext
16
- > {
17
- constructor() {
18
- super(
19
- new RequestAdapterFetch({
20
- baseURL: '/api/admin',
21
- responseType: 'json'
22
- })
23
- );
24
- }
25
- }
@@ -1,78 +0,0 @@
1
- import { inject, injectable } from 'inversify';
2
- import type {
3
- AppUserApiInterface,
4
- UserApiLoginTransaction,
5
- UserApiLogoutTransaction,
6
- UserApiRegisterTransaction
7
- } from '@/base/port/AppUserApiInterface';
8
- import { AppApiRequester } from './AppApiRequester';
9
- import type { AppApiConfig, AppApiRequesterContext } from './AppApiRequester';
10
- import { RequestExecutor } from '@qlover/fe-corekit';
11
-
12
- /**
13
- * UserApi
14
- *
15
- * @description
16
- * UserApi is a client for the user API.
17
- *
18
- */
19
- @injectable()
20
- export class AppUserApi implements AppUserApiInterface {
21
- constructor(
22
- @inject(AppApiRequester)
23
- protected client: RequestExecutor<AppApiConfig, AppApiRequesterContext>
24
- ) {}
25
-
26
- /**
27
- * @override
28
- */
29
- public async login(
30
- params: UserApiLoginTransaction['data']
31
- ): Promise<UserApiLoginTransaction['response']> {
32
- const response = await this.client.request<
33
- UserApiLoginTransaction['response'],
34
- UserApiLoginTransaction['request']
35
- >({
36
- url: '/user/login',
37
- method: 'POST',
38
- data: params,
39
- encryptProps: 'password'
40
- });
41
-
42
- return response;
43
- }
44
-
45
- /**
46
- * @override
47
- */
48
- public async register(
49
- params: UserApiRegisterTransaction['data']
50
- ): Promise<UserApiRegisterTransaction['response']> {
51
- const response = await this.client.request<
52
- UserApiRegisterTransaction['response'],
53
- UserApiRegisterTransaction['request']
54
- >({
55
- url: '/user/register',
56
- method: 'POST',
57
- data: params,
58
- encryptProps: 'password'
59
- });
60
-
61
- return response;
62
- }
63
-
64
- /**
65
- * @override
66
- */
67
- public async logout(
68
- _params?: unknown
69
- ): Promise<UserApiLogoutTransaction['response']> {
70
- return await this.client.request<
71
- UserApiLogoutTransaction['response'],
72
- UserApiLogoutTransaction['request']
73
- >({
74
- url: '/user/logout',
75
- method: 'POST'
76
- });
77
- }
78
- }
@@ -1,8 +0,0 @@
1
- import type { UserSchema } from '@migrations/schema/UserSchema';
2
- import type { LoginValidatorData } from '../validators/LoginValidator';
3
-
4
- export interface UserControllerInerface {
5
- login(body: LoginValidatorData): Promise<UserSchema>;
6
- register(body: LoginValidatorData): Promise<UserSchema>;
7
- logout(): Promise<void>;
8
- }