@qlover/create-app 0.9.0 → 0.10.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.
Files changed (57) 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/.env.template +9 -10
  5. package/dist/templates/next-app/eslint.config.mjs +5 -0
  6. package/dist/templates/next-app/next.config.ts +1 -1
  7. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
  8. package/dist/templates/next-app/src/app/[locale]/page.tsx +1 -1
  9. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
  10. package/dist/templates/next-app/src/app/api/locales/json/route.ts +2 -1
  11. package/dist/templates/next-app/src/i18n/request.ts +2 -2
  12. package/dist/templates/react-app/__tests__/__mocks__/{MockAppConfit.ts → MockAppConfig.ts} +1 -1
  13. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +10 -17
  14. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +27 -8
  15. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +1 -1
  16. package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +110 -0
  17. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +3 -2
  18. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +13 -0
  19. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +3 -1
  20. package/dist/templates/react-app/config/IOCIdentifier.ts +9 -6
  21. package/dist/templates/react-app/config/common.ts +38 -0
  22. package/dist/templates/react-app/config/feapi.mock.json +5 -12
  23. package/dist/templates/react-app/eslint.config.mjs +6 -5
  24. package/dist/templates/react-app/package.json +1 -1
  25. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +22 -13
  26. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +3 -3
  27. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +17 -12
  28. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +19 -2
  29. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +2 -4
  30. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +15 -9
  31. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +55 -0
  32. package/dist/templates/react-app/src/base/services/I18nService.ts +1 -0
  33. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +43 -0
  34. package/dist/templates/react-app/src/base/services/UserGatewayPlugin.ts +16 -0
  35. package/dist/templates/react-app/src/base/services/UserService.ts +51 -80
  36. package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +8 -3
  37. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +6 -6
  38. package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +28 -0
  39. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +24 -18
  40. package/dist/templates/react-app/src/core/globals.ts +10 -11
  41. package/dist/templates/react-app/src/main.tsx +1 -1
  42. package/dist/templates/react-app/src/pages/auth/Layout.tsx +4 -4
  43. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +1 -1
  44. package/dist/templates/react-app/src/pages/base/Layout.tsx +3 -3
  45. package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +44 -0
  46. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +1 -3
  47. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +9 -0
  48. package/dist/templates/react-app/src/uikit/hooks/{useI18nGuard.ts → useRouterI18nGuard.ts} +7 -4
  49. package/dist/templates/react-app/tsconfig.app.json +4 -2
  50. package/dist/templates/react-app/tsconfig.node.json +4 -0
  51. package/dist/templates/react-app/tsconfig.test.json +3 -1
  52. package/package.json +3 -3
  53. package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +0 -102
  54. package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +0 -61
  55. package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +0 -57
  56. package/dist/templates/react-app/src/uikit/components/ProcessExecutorProvider.tsx +0 -28
  57. package/dist/templates/react-app/src/uikit/components/UserAuthProvider.tsx +0 -16
@@ -7,10 +7,7 @@ import unusedImports from 'eslint-plugin-unused-imports';
7
7
  import qloverEslint from '@qlover/eslint-plugin';
8
8
  import tseslint from 'typescript-eslint';
9
9
  import globals from 'globals';
10
- import {
11
- disableGlobals,
12
- restrictSpecificGlobals
13
- } from './makes/eslint-utils.mjs';
10
+ import { restrictSpecificGlobals } from './makes/eslint-utils.mjs';
14
11
 
15
12
  const __filename = fileURLToPath(import.meta.url);
16
13
  const __dirname = dirname(__filename);
@@ -186,10 +183,14 @@ const eslintConfig = [
186
183
  'prettier/prettier': [
187
184
  'error',
188
185
  {
189
- semi: true,
190
186
  singleQuote: true,
191
187
  trailingComma: 'none',
192
188
  endOfLine: 'lf'
189
+ },
190
+ {
191
+ // 仅用于单独部署时对 eslint prettier 插件自动查找 prettierrc 时报错
192
+ // 注意: vscode 等编辑器会失效, 作为单独项目开发时可以去掉
193
+ usePrettierrc: false
193
194
  }
194
195
  ],
195
196
  // 默认禁用 export default
@@ -32,7 +32,7 @@
32
32
  "dev:force": "vite --mode localhost --force",
33
33
  "dev:staging": "vite --mode staging",
34
34
  "dev:prod": "vite --mode production",
35
- "build": "npm run lint && vite build",
35
+ "build": "vite build",
36
36
  "build:staging": "npm run lint && vite build --mode staging",
37
37
  "build:prod": "npm run lint && vite build --mode production",
38
38
  "build:analyze": "vite build --mode production && start dist/stats.html",
@@ -6,20 +6,20 @@ import {
6
6
  } from '@qlover/fe-corekit';
7
7
  import { inject, injectable } from 'inversify';
8
8
  import { AppError } from '@/base/cases/AppError';
9
- import { RegisterFormData } from '@/base/services/UserService';
10
9
  import { UserApiAdapter } from './UserApiAdapter';
11
10
  import { UserApiConfig } from './UserApiBootstarp';
12
11
  import {
13
12
  GetIpInfoTransaction,
14
13
  UserApiGetUserInfoTransaction,
15
14
  UserApiLoginTransaction,
15
+ UserApiRegisterTransaction,
16
16
  UserApiTestApiCatchResultTransaction,
17
- UserInfo
17
+ UserInfo,
18
+ UserCredential
18
19
  } from './UserApiType';
19
20
  import type {
20
- LoginResponseData,
21
- UserAuthApiInterface,
22
- UserAuthStoreInterface
21
+ UserAuthStoreInterface,
22
+ UserServiceGateway
23
23
  } from '@qlover/corekit-bridge';
24
24
 
25
25
  /**
@@ -32,7 +32,7 @@ import type {
32
32
  @injectable()
33
33
  export class UserApi
34
34
  extends RequestTransaction<UserApiConfig>
35
- implements UserAuthApiInterface<UserInfo>
35
+ implements UserServiceGateway<UserInfo, UserCredential>
36
36
  {
37
37
  protected store: UserAuthStoreInterface<UserInfo> | null = null;
38
38
 
@@ -85,7 +85,7 @@ export class UserApi
85
85
  */
86
86
  async login(
87
87
  params: UserApiLoginTransaction['data']
88
- ): Promise<LoginResponseData> {
88
+ ): Promise<UserCredential> {
89
89
  const response = await this.post<UserApiLoginTransaction>(
90
90
  '/api/login',
91
91
  params
@@ -108,24 +108,33 @@ export class UserApi
108
108
  * @returns
109
109
  */
110
110
  register(
111
- params: RegisterFormData
112
- ): Promise<UserApiLoginTransaction['response']> {
113
- return this.post<UserApiLoginTransaction>('/api/register', params);
111
+ params: UserApiRegisterTransaction['data']
112
+ ): Promise<UserApiRegisterTransaction['response']['data']> {
113
+ // @ts-expect-error - TODO: implement
114
+ return this.post<UserApiRegisterTransaction>('/api/register', params);
114
115
  }
115
116
 
116
117
  /**
117
118
  * @override
118
119
  * @returns
119
120
  */
120
- logout(): Promise<void> {
121
- return Promise.resolve();
121
+ async refreshUserInfo(): Promise<UserInfo> {
122
+ return this.getUserInfo();
122
123
  }
123
124
 
124
125
  /**
125
126
  * @override
126
127
  * @returns
127
128
  */
128
- async getUserInfo(): Promise<UserInfo> {
129
+ logout(): Promise<any> {
130
+ return this.post('/api/logout');
131
+ }
132
+
133
+ /**
134
+ * @override
135
+ * @returns
136
+ */
137
+ async getUserInfo(_credential?: UserCredential): Promise<UserInfo> {
129
138
  const response =
130
139
  await this.get<UserApiGetUserInfoTransaction>('/api/userinfo');
131
140
 
@@ -2,7 +2,7 @@ import { IOCIdentifier } from '@config/IOCIdentifier';
2
2
  import {
3
3
  type BootstrapContext,
4
4
  type BootstrapExecutorPlugin,
5
- type ApiMockPluginConfig,
5
+ type ApiMockPluginOptions,
6
6
  type ApiCatchPluginConfig,
7
7
  type ApiCatchPluginResponse
8
8
  } from '@qlover/corekit-bridge';
@@ -23,12 +23,12 @@ import type {
23
23
  * UserApiConfig is the config for the UserApi.
24
24
  *
25
25
  * extends:
26
- * - ApiMockPluginConfig
26
+ * - ApiMockPluginOptions
27
27
  * - ApiCatchPluginConfig
28
28
  */
29
29
  export interface UserApiConfig<Request = unknown>
30
30
  extends RequestAdapterConfig<Request>,
31
- ApiMockPluginConfig,
31
+ ApiMockPluginOptions,
32
32
  ApiCatchPluginConfig {}
33
33
 
34
34
  /**
@@ -1,4 +1,5 @@
1
1
  import type { UserApiTransaction } from './UserApiBootstarp';
2
+ import type { LoginParams } from '@qlover/corekit-bridge';
2
3
 
3
4
  export type UserInfo = {
4
5
  name: string;
@@ -44,22 +45,26 @@ export type UserApiGetUserInfoTransaction = UserApiTransaction<
44
45
  UserInfo
45
46
  >;
46
47
 
48
+ export type UserCredential = {
49
+ token: string;
50
+ };
51
+
47
52
  export type UserApiLoginTransaction = UserApiTransaction<
48
- { username: string; password: string },
49
- {
50
- token: string;
51
- }
53
+ LoginParams & { username?: string },
54
+ UserCredential
52
55
  >;
53
56
 
54
57
  export type UserApiTestApiCatchResultTransaction = UserApiGetRandomUser;
55
58
 
59
+ export interface RegisterFormData {
60
+ username: string;
61
+ email: string;
62
+ password: string;
63
+ confirmPassword: string;
64
+ agreeToTerms: boolean;
65
+ }
66
+
56
67
  export type UserApiRegisterTransaction = UserApiTransaction<
57
- {
58
- username: string;
59
- email: string;
60
- password: string;
61
- confirmPassword: string;
62
- agreeToTerms: boolean;
63
- },
64
- UserApiTransaction['response']['data']
68
+ RegisterFormData,
69
+ UserInfo
65
70
  >;
@@ -1,7 +1,11 @@
1
1
  import { IOCIdentifier } from '@config/IOCIdentifier';
2
+ import {
3
+ ExecutorError,
4
+ type ExecutorContext,
5
+ type ExecutorPlugin
6
+ } from '@qlover/fe-corekit';
2
7
  import { inject, injectable } from 'inversify';
3
8
  import type { I18nServiceInterface } from '../port/I18nServiceInterface';
4
- import type { ExecutorContext, ExecutorPlugin } from '@qlover/fe-corekit';
5
9
  import type { LoggerInterface } from '@qlover/logger';
6
10
 
7
11
  /**
@@ -19,14 +23,27 @@ export class I18nKeyErrorPlugin implements ExecutorPlugin {
19
23
  protected i18nService: I18nServiceInterface
20
24
  ) {}
21
25
 
26
+ protected translateById(id: string): string {
27
+ return this.i18nService.t(id);
28
+ }
29
+
22
30
  onError(context: ExecutorContext<unknown>): Error | void {
23
31
  const { error } = context;
24
32
 
33
+ if (!error) {
34
+ return;
35
+ }
36
+
37
+ if (error instanceof ExecutorError) {
38
+ const i18nText = this.translateById(error.id);
39
+ return new ExecutorError(error.id, i18nText);
40
+ }
41
+
25
42
  if (error instanceof Error) {
26
43
  const i18nKey = error.message;
27
44
 
28
45
  if (i18nKey && typeof i18nKey === 'string') {
29
- const i18nText = this.i18nService.t(i18nKey);
46
+ const i18nText = this.translateById(i18nKey);
30
47
 
31
48
  if (i18nText && i18nText !== i18nKey) {
32
49
  this.logger.debug('I18nKeyErrorPlugin Error:', i18nText);
@@ -1,9 +1,7 @@
1
1
  import { StoreInterface } from '@qlover/corekit-bridge';
2
2
  import type { RouteConfigValue } from '../cases/RouterLoader';
3
- import type {
4
- LoggerInterface,
5
- StoreStateInterface
6
- } from '@qlover/corekit-bridge';
3
+ import type { StoreStateInterface } from '@qlover/corekit-bridge';
4
+ import type { LoggerInterface } from '@qlover/logger';
7
5
  import type { NavigateFunction, NavigateOptions } from 'react-router-dom';
8
6
 
9
7
  export interface RouteServiceStateInterface extends StoreStateInterface {
@@ -1,12 +1,18 @@
1
- import { UserAuthService } from '@qlover/corekit-bridge';
2
- import type { UserInfo } from '@/base/apis/userApi/UserApiType';
3
- import type { ExecutorPlugin } from '@qlover/fe-corekit';
1
+ import type { UserInfo, UserCredential } from '@/base/apis/userApi/UserApiType';
2
+ import type { UserServiceInterface as CorekitBridgeUserServiceInterface } from '@qlover/corekit-bridge';
4
3
 
5
- export abstract class UserServiceInterface
6
- extends UserAuthService<UserInfo>
7
- implements ExecutorPlugin
8
- {
9
- readonly pluginName = 'UserService';
4
+ export interface UserServiceInterface
5
+ extends CorekitBridgeUserServiceInterface<UserInfo, UserCredential> {
6
+ // You can add your own methods here
10
7
 
11
- abstract getToken(): string | null;
8
+ /**
9
+ * Get the user token
10
+ *
11
+ * This is a extends method from the corekit-bridge UserServiceInterface.
12
+ */
13
+ getToken(): string;
14
+
15
+ isUserInfo(value: unknown): value is UserInfo;
16
+
17
+ isUserCredential(value: unknown): value is UserCredential;
12
18
  }
@@ -0,0 +1,55 @@
1
+ import { I } from '@config/IOCIdentifier';
2
+ import { AsyncExecutor, ExecutorPlugin } from '@qlover/fe-corekit';
3
+ import { injectable, inject } from 'inversify';
4
+ import { UserBootstrap } from './UserBootstrap';
5
+ import type { RouteServiceInterface } from '../port/RouteServiceInterface';
6
+ import type { UserServiceInterface } from '../port/UserServiceInterface';
7
+ import type {
8
+ BootstrapContextValue,
9
+ IOCContainerInterface
10
+ } from '@qlover/corekit-bridge';
11
+ import type { LoggerInterface } from '@qlover/logger';
12
+
13
+ @injectable()
14
+ export class BaseLayoutService {
15
+ protected executor: AsyncExecutor = new AsyncExecutor();
16
+
17
+ constructor(
18
+ @inject(I.Logger) protected logger: LoggerInterface,
19
+ @inject(I.RouteServiceInterface)
20
+ protected routeService: RouteServiceInterface,
21
+ @inject(I.UserServiceInterface)
22
+ protected userService: UserServiceInterface
23
+ ) {
24
+ this.use(new UserBootstrap(userService));
25
+ }
26
+
27
+ use(plugin: ExecutorPlugin): this {
28
+ this.executor.use(plugin);
29
+ return this;
30
+ }
31
+
32
+ handler(): Promise<{ success: boolean }> {
33
+ return Promise.resolve({
34
+ success: true
35
+ });
36
+ }
37
+
38
+ async starup(ioc: IOCContainerInterface): Promise<unknown> {
39
+ const context: BootstrapContextValue = {
40
+ root: {},
41
+ logger: this.logger,
42
+ ioc: ioc
43
+ };
44
+
45
+ try {
46
+ const result = await this.executor.exec(context, this.handler.bind(this));
47
+ this.logger.info('BaseLayoutService starup success!');
48
+ return result;
49
+ } catch (err) {
50
+ this.logger.error('BaseLayoutService starup failed!', err);
51
+
52
+ this.routeService.gotoLogin();
53
+ }
54
+ }
55
+ }
@@ -119,6 +119,7 @@ export class I18nService
119
119
  t(key: string, params?: Record<string, unknown>): string {
120
120
  const i18nValue = i18n.t(key, {
121
121
  lng: i18n.language,
122
+ nsSeparator: false,
122
123
  ...params
123
124
  });
124
125
 
@@ -0,0 +1,43 @@
1
+ import { LOCAL_NO_USER_TOKEN } from '@config/Identifier';
2
+ import { I } from '@config/IOCIdentifier';
3
+ import { inject, injectable } from 'inversify';
4
+ import { AppError } from '../cases/AppError';
5
+ import type { UserServiceInterface } from '../port/UserServiceInterface';
6
+ import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
7
+
8
+ @injectable()
9
+ export class UserBootstrap implements BootstrapExecutorPlugin {
10
+ readonly pluginName = 'UserBootstrap';
11
+
12
+ constructor(
13
+ @inject(I.UserServiceInterface)
14
+ protected userService: UserServiceInterface
15
+ ) {}
16
+
17
+ async onBefore(): Promise<void> {
18
+ const userService = this.userService;
19
+
20
+ if (userService.isAuthenticated()) {
21
+ return;
22
+ }
23
+
24
+ // 如果用户凭证存在,则刷新用户信息, 更新状态
25
+ const credential = userService.getCredential();
26
+ if (userService.isUserCredential(credential)) {
27
+ userService.getStore().start();
28
+
29
+ const user = await userService.refreshUserInfo();
30
+
31
+ userService.getStore().success(user);
32
+
33
+ return;
34
+ }
35
+
36
+ const newError = new AppError(LOCAL_NO_USER_TOKEN);
37
+
38
+ userService.getStore().failed(newError);
39
+
40
+ // 否则还是抛出错误
41
+ throw newError;
42
+ }
43
+ }
@@ -0,0 +1,16 @@
1
+ import type { UserInfo, UserCredential } from '@/base/apis/userApi/UserApiType';
2
+ import type { RouteServiceInterface } from '../port/RouteServiceInterface';
3
+ import type { UserServicePluginInterface } from '@qlover/corekit-bridge';
4
+
5
+ export class UserGatewayPlugin
6
+ implements UserServicePluginInterface<UserInfo, UserCredential>
7
+ {
8
+ readonly pluginName = 'UserGatewayPlugin';
9
+
10
+ constructor(protected routerService: RouteServiceInterface) {}
11
+
12
+ onLogoutSuccess(): void {
13
+ this.routerService.reset();
14
+ this.routerService.gotoLogin();
15
+ }
16
+ }
@@ -1,51 +1,41 @@
1
- import * as errKeys from '@config/Identifier/common/common.error';
2
- import { IOCIdentifier } from '@config/IOCIdentifier';
1
+ import { I } from '@config/IOCIdentifier';
3
2
  import {
4
- type UserAuthApiInterface,
5
- type UserAuthState,
6
- LoginResponseData,
7
- UserAuthStore
3
+ UserService as CorekitBridgeUserService,
4
+ GatewayExecutor
8
5
  } from '@qlover/corekit-bridge';
9
- import { type SyncStorageInterface } from '@qlover/fe-corekit';
10
6
  import { inject, injectable } from 'inversify';
7
+ import { isObject, isString } from 'lodash';
11
8
  import { UserApi } from '@/base/apis/userApi/UserApi';
12
- import type {
13
- UserApiLoginTransaction,
14
- UserInfo
15
- } from '@/base/apis/userApi/UserApiType';
16
- import { AppError } from '@/base/cases/AppError';
9
+ import type { UserInfo, UserCredential } from '@/base/apis/userApi/UserApiType';
17
10
  import { AppConfig } from '../cases/AppConfig';
18
- import { RouteServiceInterface } from '../port/RouteServiceInterface';
19
11
  import { UserServiceInterface } from '../port/UserServiceInterface';
20
-
21
- export interface UserApiState extends UserAuthState<UserInfo> {}
22
-
23
- export interface RegisterFormData {
24
- username: string;
25
- email: string;
26
- password: string;
27
- confirmPassword: string;
28
- agreeToTerms: boolean;
29
- }
12
+ import type { UserServiceGateway } from '@qlover/corekit-bridge';
13
+ import type { SyncStorageInterface } from '@qlover/fe-corekit';
14
+ import type { LoggerInterface } from '@qlover/logger';
30
15
 
31
16
  @injectable()
32
- export class UserService extends UserServiceInterface {
17
+ export class UserService
18
+ extends CorekitBridgeUserService<UserInfo, UserCredential>
19
+ implements UserServiceInterface
20
+ {
33
21
  constructor(
34
- @inject(IOCIdentifier.RouteServiceInterface)
35
- protected routerService: RouteServiceInterface,
36
22
  @inject(UserApi)
37
- userApi: UserAuthApiInterface<UserInfo>,
38
- @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
39
- @inject(IOCIdentifier.LocalStorageEncrypt)
40
- storage: SyncStorageInterface<string, string>
23
+ userApi: UserServiceGateway<UserInfo, UserCredential>,
24
+ @inject(I.AppConfig) appConfig: AppConfig,
25
+ @inject(I.LocalStorageEncrypt)
26
+ storage: SyncStorageInterface<string>,
27
+ @inject(I.Logger)
28
+ logger: LoggerInterface
41
29
  ) {
42
- super(userApi, {
43
- userStorage: {
44
- key: appConfig.userInfoStorageKey,
45
- storage: storage
46
- },
47
- credentialStorage: {
48
- key: appConfig.userTokenStorageKey,
30
+ super({
31
+ executor: new GatewayExecutor(),
32
+ gateway: userApi,
33
+ logger: logger,
34
+ store: {
35
+ storageKey: appConfig.userInfoStorageKey,
36
+ credentialStorageKey: appConfig.userTokenStorageKey,
37
+ // Not production environment persists user information, for testing and development
38
+ persistUserInfo: !appConfig.isProduction,
49
39
  storage: storage
50
40
  }
51
41
  });
@@ -53,60 +43,41 @@ export class UserService extends UserServiceInterface {
53
43
 
54
44
  /**
55
45
  * @override
46
+ * @returns
56
47
  */
57
- override get store(): UserAuthStore<UserApiState> {
58
- return super.store as UserAuthStore<UserApiState>;
48
+ getToken(): string {
49
+ return this.getCredential()?.token ?? '';
59
50
  }
60
51
 
61
- override getToken(): string | null {
62
- return this.store.getCredential();
52
+ isUserInfo(value: unknown): value is UserInfo {
53
+ return (
54
+ isObject(value) &&
55
+ 'name' in value &&
56
+ isString(value.name) &&
57
+ 'email' in value &&
58
+ isString(value.email) &&
59
+ 'picture' in value &&
60
+ isString(value.picture)
61
+ );
63
62
  }
64
63
 
65
- /**
66
- * @override
67
- */
68
- async onBefore(): Promise<void> {
69
- if (this.isAuthenticated()) {
70
- return;
71
- }
72
-
73
- const userToken = this.getToken();
64
+ isUserCredential(value: unknown): value is UserCredential {
65
+ return isObject(value) && 'token' in value && isString(value.token);
66
+ }
74
67
 
75
- if (!userToken) {
76
- throw new AppError(errKeys.LOCAL_NO_USER_TOKEN);
68
+ override isAuthenticated(): boolean {
69
+ if (!super.isAuthenticated()) {
70
+ return false;
77
71
  }
78
72
 
79
- if (userToken) {
80
- this.store.authSuccess();
73
+ if (!this.isUserInfo(this.getUser())) {
74
+ return false;
81
75
  }
82
76
 
83
- await this.userInfo();
84
- this.store.authSuccess();
85
- }
86
-
87
- override async logout(): Promise<void> {
88
- await super.logout();
89
-
90
- this.routerService.reset();
91
- this.routerService.gotoLogin();
92
- }
93
-
94
- override async register(
95
- params: RegisterFormData
96
- ): Promise<LoginResponseData> {
97
- const response = (await this.api.register(
98
- params
99
- )) as UserApiLoginTransaction['response'];
100
-
101
- if (response.data?.token) {
102
- try {
103
- await this.userInfo({ token: response.data?.token });
104
- this.store.authSuccess();
105
- } catch (error) {
106
- this.store.authFailed(error);
107
- }
77
+ if (!this.isUserCredential(this.getCredential())) {
78
+ return false;
108
79
  }
109
80
 
110
- throw new AppError(errKeys.LOCAL_NO_USER_TOKEN);
81
+ return true;
111
82
  }
112
83
  }
@@ -1,6 +1,11 @@
1
- import { envBlackList, envPrefix, browserGlobalsName } from '@config/common';
1
+ import {
2
+ envBlackList,
3
+ envPrefix,
4
+ browserGlobalsName,
5
+ omitInjectedGlobals
6
+ } from '@config/common';
2
7
  import { Bootstrap } from '@qlover/corekit-bridge';
3
- import { isObject } from 'lodash';
8
+ import { isObject, omit } from 'lodash';
4
9
  import type { IOCInterface, IOCRegister } from '@/base/port/IOCInterface';
5
10
  import type { IOCIdentifierMap } from '@config/IOCIdentifier';
6
11
  import * as globals from '../globals';
@@ -78,7 +83,7 @@ export class BootstrapClient {
78
83
  blackList: envBlackList
79
84
  },
80
85
  globalOptions: {
81
- sources: globals,
86
+ sources: omit(globals, omitInjectedGlobals),
82
87
  target: browserGlobalsName
83
88
  }
84
89
  });
@@ -1,10 +1,11 @@
1
- import { IOCIdentifier } from '@config/IOCIdentifier';
1
+ import { I } from '@config/IOCIdentifier';
2
2
  import { AiApiBootstarp } from '@/base/apis/AiApi';
3
3
  import { FeApiBootstarp } from '@/base/apis/feApi/FeApiBootstarp';
4
4
  import { UserApiBootstarp } from '@/base/apis/userApi/UserApiBootstarp';
5
5
  import type { IOCIdentifierMap } from '@config/IOCIdentifier';
6
6
  import { IocIdentifierTest } from './IocIdentifierTest';
7
7
  import { printBootstrap } from './PrintBootstrap';
8
+ import { saveAppInfo } from './SaveAppInfo';
8
9
  import type {
9
10
  BootstrapExecutorPlugin,
10
11
  EnvConfigInterface,
@@ -24,18 +25,19 @@ export class BootstrapsRegistry implements BootstrapsRegistryInterface {
24
25
  ) {}
25
26
 
26
27
  get appConfig(): EnvConfigInterface {
27
- return this.IOC(IOCIdentifier.AppConfig);
28
+ return this.IOC(I.AppConfig);
28
29
  }
29
30
 
30
31
  register(): BootstrapExecutorPlugin[] {
31
32
  const IOC = this.IOC;
32
33
 
33
34
  const bootstrapList = [
34
- IOC(IOCIdentifier.I18nServiceInterface),
35
+ IOC(I.I18nServiceInterface),
35
36
  new UserApiBootstarp(),
36
37
  new FeApiBootstarp(),
37
38
  AiApiBootstarp,
38
- IOC(IOCIdentifier.I18nKeyErrorPlugin)
39
+ saveAppInfo,
40
+ IOC(I.I18nKeyErrorPlugin)
39
41
  ];
40
42
 
41
43
  if (!this.appConfig.isProduction) {
@@ -43,8 +45,6 @@ export class BootstrapsRegistry implements BootstrapsRegistryInterface {
43
45
  }
44
46
 
45
47
  bootstrapList.push(IocIdentifierTest);
46
- // TODO: 需要使用到
47
- bootstrapList.push(IOC(IOCIdentifier.ProcesserExecutorInterface));
48
48
 
49
49
  return bootstrapList;
50
50
  }
@@ -0,0 +1,28 @@
1
+ import { I } from '@config/IOCIdentifier';
2
+ import type { AppConfig } from '@/base/cases/AppConfig';
3
+ import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
4
+ import type { SyncStorageInterface } from '@qlover/fe-corekit';
5
+
6
+ /**
7
+ * 保存应用信息到本地存储
8
+ *
9
+ * 包含以下:
10
+ *
11
+ * - 应用名称
12
+ * - 应用版本
13
+ */
14
+ export const saveAppInfo: BootstrapExecutorPlugin = {
15
+ pluginName: 'SaveAppInfo',
16
+ onSuccess({ parameters: { ioc, logger } }) {
17
+ const appConfig = ioc.get<AppConfig>(I.AppConfig);
18
+ const localStorage = ioc.get<SyncStorageInterface<string>>(I.LocalStorage);
19
+
20
+ const key = appConfig.appName + '_appInfo';
21
+ localStorage.setItem(key, {
22
+ appName: appConfig.appName,
23
+ appVersion: appConfig.appVersion
24
+ });
25
+
26
+ logger.info(`Saved app info to localStorage: ${key}`);
27
+ }
28
+ };