@qlover/create-app 0.7.10 → 0.7.12

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 (92) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/dist/index.cjs +6 -6
  3. package/dist/index.js +6 -6
  4. package/dist/templates/next-app/config/Identifier/api.ts +14 -0
  5. package/dist/templates/next-app/config/Identifier/common.ts +7 -0
  6. package/dist/templates/next-app/eslint.config.mjs +17 -0
  7. package/dist/templates/next-app/migrations/schema/UserSchema.ts +21 -10
  8. package/dist/templates/next-app/next.config.ts +1 -0
  9. package/dist/templates/next-app/package.json +1 -0
  10. package/dist/templates/next-app/public/locales/en.json +4 -1
  11. package/dist/templates/next-app/public/locales/zh.json +4 -1
  12. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +4 -7
  13. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +16 -4
  14. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +62 -0
  15. package/dist/templates/next-app/src/app/[locale]/layout.tsx +1 -1
  16. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
  17. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
  18. package/dist/templates/next-app/src/app/[locale]/page.tsx +3 -3
  19. package/dist/templates/next-app/src/app/[locale]/register/RegisterForm.tsx +1 -1
  20. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
  21. package/dist/templates/next-app/src/app/api/admin/users/route.ts +39 -0
  22. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +40 -0
  23. package/dist/templates/next-app/src/base/cases/AppConfig.ts +1 -1
  24. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +13 -2
  25. package/dist/templates/next-app/src/base/cases/PageParams.ts +1 -1
  26. package/dist/templates/next-app/src/base/cases/RequestState.ts +20 -0
  27. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +1 -1
  28. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +26 -0
  29. package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +87 -0
  30. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +1 -1
  31. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +4 -4
  32. package/dist/templates/next-app/src/base/port/AsyncStateInterface.ts +7 -0
  33. package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +3 -0
  34. package/dist/templates/next-app/src/base/port/PaginationInterface.ts +6 -0
  35. package/dist/templates/next-app/src/base/services/AdminUserService.ts +45 -0
  36. package/dist/templates/next-app/src/base/services/UserService.ts +1 -1
  37. package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +21 -0
  38. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +34 -0
  39. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +2 -2
  40. package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +56 -0
  41. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +30 -31
  42. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +5 -4
  43. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +1 -1
  44. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +3 -14
  45. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +2 -2
  46. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +1 -1
  47. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +1 -1
  48. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +1 -1
  49. package/dist/templates/next-app/src/core/globals.ts +1 -1
  50. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +1 -1
  51. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +1 -1
  52. package/dist/templates/next-app/src/server/ServerAuth.ts +13 -3
  53. package/dist/templates/next-app/src/server/SupabaseBridge.ts +37 -2
  54. package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -1
  55. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +10 -0
  56. package/dist/templates/next-app/src/server/port/{UserAuthInterface.ts → ServerAuthInterface.ts} +3 -1
  57. package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +1 -1
  58. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +32 -1
  59. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +19 -0
  60. package/dist/templates/next-app/src/server/services/ApiUserService.ts +26 -0
  61. package/dist/templates/next-app/src/server/services/UserService.ts +3 -3
  62. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +48 -0
  63. package/dist/templates/next-app/src/styles/css/antd-themes/{_default.css → _common/_default.css} +74 -1
  64. package/dist/templates/next-app/src/styles/css/antd-themes/{dark.css → _common/dark.css} +73 -0
  65. package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +3 -0
  66. package/dist/templates/next-app/src/styles/css/antd-themes/{pink.css → _common/pink.css} +70 -0
  67. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +4 -3
  68. package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +108 -0
  69. package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +67 -0
  70. package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +3 -0
  71. package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +67 -0
  72. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +33 -0
  73. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +32 -0
  74. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +3 -0
  75. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +35 -0
  76. package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +44 -0
  77. package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +43 -0
  78. package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +3 -0
  79. package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +43 -0
  80. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +106 -0
  81. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +68 -17
  82. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +6 -1
  83. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +1 -1
  84. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +11 -3
  85. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +1 -1
  86. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +1 -1
  87. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +1 -1
  88. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +7 -1
  89. package/package.json +1 -1
  90. package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +0 -27
  91. package/dist/templates/next-app/src/base/port/DBTableInterface.ts +0 -3
  92. package/dist/templates/next-app/src/base/services/appApi/AppUserType.ts +0 -51
@@ -0,0 +1,45 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import {
3
+ AdminPageInterface,
4
+ type AdminPageListParams,
5
+ AdminPageState
6
+ } from '../port/AdminPageInterface';
7
+ import { AdminUserApi } from './adminApi/AdminUserApi';
8
+ import { RequestState } from '../cases/RequestState';
9
+ import type { PaginationInterface } from '../port/PaginationInterface';
10
+
11
+ @injectable()
12
+ export class AdminUserService extends AdminPageInterface<AdminPageState> {
13
+ constructor(@inject(AdminUserApi) protected adminUserApi: AdminUserApi) {
14
+ super(() => new AdminPageState());
15
+ }
16
+
17
+ override async fetchList(
18
+ params: Partial<AdminPageListParams>
19
+ ): Promise<PaginationInterface<unknown>> {
20
+ this.changeListState(new RequestState(true));
21
+
22
+ try {
23
+ const response = await this.adminUserApi.getUserList(
24
+ Object.assign({}, this.state.listParams, params)
25
+ );
26
+
27
+ if (response.data.success) {
28
+ const paginationData = response.data
29
+ .data as PaginationInterface<unknown>;
30
+
31
+ this.changeListState(new RequestState(false, paginationData));
32
+
33
+ return paginationData;
34
+ }
35
+
36
+ this.changeListState(
37
+ new RequestState(false, null, response.data.message)
38
+ );
39
+ } catch (error) {
40
+ this.changeListState(new RequestState(false, null, error));
41
+ }
42
+
43
+ return this.state.listState.result!;
44
+ }
45
+ }
@@ -1,8 +1,8 @@
1
1
  import { injectable, inject } from 'inversify';
2
+ import type { UserSchema } from '@migrations/schema/UserSchema';
2
3
  import { AppConfig } from '../cases/AppConfig';
3
4
  import { UserServiceApi } from '../cases/UserServiceApi';
4
5
  import { UserServiceInterface } from '../port/UserServiceInterface';
5
- import type { UserSchema } from '@migrations/schema/UserSchema';
6
6
  import type { UserAuthApiInterface } from '@qlover/corekit-bridge';
7
7
 
8
8
  @injectable()
@@ -0,0 +1,21 @@
1
+ import {
2
+ FetchAbortPlugin,
3
+ RequestAdapterFetch,
4
+ RequestTransaction
5
+ } from '@qlover/fe-corekit';
6
+ import { inject, injectable } from 'inversify';
7
+ import type { AppApiConfig } from '../appApi/AppApiRequester';
8
+
9
+ @injectable()
10
+ export class AdminApiRequester extends RequestTransaction<AppApiConfig> {
11
+ constructor(
12
+ @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
13
+ ) {
14
+ super(
15
+ new RequestAdapterFetch({
16
+ baseURL: '/api/admin',
17
+ responseType: 'json'
18
+ })
19
+ );
20
+ }
21
+ }
@@ -0,0 +1,34 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import type { AdminPageListParams } from '@/base/port/AdminPageInterface';
3
+ import type { PaginationInterface } from '@/base/port/PaginationInterface';
4
+ import {
5
+ AppApiRequester,
6
+ type AppApiConfig,
7
+ type AppApiTransaction
8
+ } from '../appApi/AppApiRequester';
9
+ import type { RequestTransaction } from '@qlover/fe-corekit';
10
+
11
+ export type AdminUserListTransaction = AppApiTransaction<
12
+ AdminPageListParams,
13
+ PaginationInterface<unknown>
14
+ >;
15
+
16
+ @injectable()
17
+ export class AdminUserApi {
18
+ constructor(
19
+ @inject(AppApiRequester)
20
+ protected client: RequestTransaction<AppApiConfig>
21
+ ) {}
22
+
23
+ async getUserList(
24
+ params: AdminPageListParams
25
+ ): Promise<AdminUserListTransaction['response']> {
26
+ const response = await this.client.request<AdminUserListTransaction>({
27
+ url: '/admin/users',
28
+ method: 'GET',
29
+ params: params as unknown as Record<string, unknown>
30
+ });
31
+
32
+ return response;
33
+ }
34
+ }
@@ -5,7 +5,7 @@ import {
5
5
  type ExecutorPlugin
6
6
  } from '@qlover/fe-corekit';
7
7
  import type { AppApiErrorInterface } from '@/base/port/AppApiInterface';
8
- import type { UserApiConfig } from './AppUserType';
8
+ import type { AppApiConfig } from './AppApiRequester';
9
9
 
10
10
  export class AppApiPlugin implements ExecutorPlugin {
11
11
  readonly pluginName = 'AppApiPlugin';
@@ -30,7 +30,7 @@ export class AppApiPlugin implements ExecutorPlugin {
30
30
  }
31
31
 
32
32
  async onError(
33
- context: ExecutorContext<UserApiConfig>
33
+ context: ExecutorContext<AppApiConfig>
34
34
  ): Promise<ExecutorError | void> {
35
35
  const { error, parameters } = context;
36
36
 
@@ -0,0 +1,56 @@
1
+ import {
2
+ FetchAbortPlugin,
3
+ RequestAdapterFetch,
4
+ RequestTransaction
5
+ } from '@qlover/fe-corekit';
6
+ import { inject, injectable } from 'inversify';
7
+ import type { RequestEncryptPluginProps } from '@/base/cases/RequestEncryptPlugin';
8
+ import type { AppApiResult } from '@/base/port/AppApiInterface';
9
+ import type {
10
+ RequestAdapterConfig,
11
+ RequestAdapterResponse,
12
+ RequestTransactionInterface
13
+ } from '@qlover/fe-corekit';
14
+
15
+ export interface AppApiConfig<Request = unknown>
16
+ extends RequestAdapterConfig<Request>,
17
+ RequestEncryptPluginProps<Request> {}
18
+
19
+ /**
20
+ * UserApiResponse
21
+ *
22
+ * @description
23
+ * UserApiResponse is the response for the UserApi.
24
+ *
25
+ * extends:
26
+ * - RequestAdapterResponse<Request, Response>
27
+ */
28
+ export type AppApiResponse<
29
+ Request = unknown,
30
+ Response = unknown
31
+ > = RequestAdapterResponse<Request, AppApiResult<Response>>;
32
+
33
+ /**
34
+ * UserApi common transaction
35
+ */
36
+ export interface AppApiTransaction<Request = unknown, Response = unknown>
37
+ extends RequestTransactionInterface<
38
+ AppApiConfig<Request>,
39
+ AppApiResponse<Request, Response>
40
+ > {
41
+ data: AppApiConfig<Request>['data'];
42
+ }
43
+
44
+ @injectable()
45
+ export class AppApiRequester extends RequestTransaction<AppApiConfig> {
46
+ constructor(
47
+ @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
48
+ ) {
49
+ super(
50
+ new RequestAdapterFetch({
51
+ baseURL: '/api',
52
+ responseType: 'json'
53
+ })
54
+ );
55
+ }
56
+ }
@@ -1,16 +1,24 @@
1
- import {
2
- FetchAbortPlugin,
3
- RequestTransaction,
4
- RequestAdapterFetch
5
- } from '@qlover/fe-corekit';
6
1
  import { inject, injectable } from 'inversify';
7
- import type { AppApiResponse } from '@/base/port/AppApiInterface';
2
+ import type { AppApiResult } from '@/base/port/AppApiInterface';
8
3
  import type { AppUserApiInterface } from '@/base/port/AppUserApiInterface';
9
- import type {
10
- UserApiConfig,
11
- UserApiLoginTransaction,
12
- UserApiRegisterTransaction
13
- } from './AppUserType';
4
+ import { AppApiRequester } from './AppApiRequester';
5
+ import type { AppApiConfig, AppApiTransaction } from './AppApiRequester';
6
+ import type { RequestTransaction } from '@qlover/fe-corekit';
7
+
8
+ export type UserApiLoginTransaction = AppApiTransaction<
9
+ { email: string; password: string },
10
+ {
11
+ token: string;
12
+ }
13
+ >;
14
+
15
+ export type UserApiRegisterTransaction = AppApiTransaction<
16
+ {
17
+ email: string;
18
+ password: string;
19
+ },
20
+ AppApiTransaction['response']['data']
21
+ >;
14
22
 
15
23
  /**
16
24
  * UserApi
@@ -20,25 +28,16 @@ import type {
20
28
  *
21
29
  */
22
30
  @injectable()
23
- export class AppUserApi
24
- extends RequestTransaction<UserApiConfig>
25
- implements AppUserApiInterface
26
- {
31
+ export class AppUserApi implements AppUserApiInterface {
27
32
  constructor(
28
- @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
29
- ) {
30
- super(
31
- new RequestAdapterFetch({
32
- baseURL: '/api',
33
- responseType: 'json'
34
- })
35
- );
36
- }
33
+ @inject(AppApiRequester)
34
+ protected client: RequestTransaction<AppApiConfig>
35
+ ) {}
37
36
 
38
37
  async login(
39
38
  params: UserApiLoginTransaction['data']
40
- ): Promise<AppApiResponse<unknown>> {
41
- const response = await this.request<UserApiLoginTransaction>({
39
+ ): Promise<AppApiResult<unknown>> {
40
+ const response = await this.client.request<UserApiLoginTransaction>({
42
41
  url: '/user/login',
43
42
  method: 'POST',
44
43
  data: params,
@@ -50,8 +49,8 @@ export class AppUserApi
50
49
 
51
50
  async register(
52
51
  params: UserApiRegisterTransaction['data']
53
- ): Promise<AppApiResponse<unknown>> {
54
- const response = await this.request<UserApiRegisterTransaction>({
52
+ ): Promise<AppApiResult<unknown>> {
53
+ const response = await this.client.request<UserApiRegisterTransaction>({
55
54
  url: '/user/register',
56
55
  method: 'POST',
57
56
  data: params,
@@ -61,12 +60,12 @@ export class AppUserApi
61
60
  return response.data;
62
61
  }
63
62
 
64
- async logout(): Promise<AppApiResponse<unknown>> {
65
- const response = await this.request({
63
+ async logout(): Promise<AppApiResult<unknown>> {
64
+ const response = await this.client.request({
66
65
  url: '/user/logout',
67
66
  method: 'POST'
68
67
  });
69
68
 
70
- return response.data as AppApiResponse<unknown>;
69
+ return response.data as AppApiResult<unknown>;
71
70
  }
72
71
  }
@@ -4,8 +4,8 @@ import { DialogErrorPlugin } from '@/base/cases/DialogErrorPlugin';
4
4
  import { RequestEncryptPlugin } from '@/base/cases/RequestEncryptPlugin';
5
5
  import { StringEncryptor } from '@/base/cases/StringEncryptor';
6
6
  import { AppApiPlugin } from './AppApiPlugin';
7
- import { AppUserApi } from './AppUserApi';
8
- import type { UserApiConfig } from './AppUserType';
7
+ import { AppApiRequester } from './AppApiRequester';
8
+ import type { AppApiConfig } from './AppApiRequester';
9
9
  import type {
10
10
  BootstrapContext,
11
11
  BootstrapExecutorPlugin
@@ -18,7 +18,7 @@ export class AppUserApiBootstrap implements BootstrapExecutorPlugin {
18
18
  constructor(protected serializer: SerializerIneterface) {}
19
19
 
20
20
  onBefore({ parameters: { ioc } }: BootstrapContext): void | Promise<void> {
21
- const appUserApi = ioc.get<AppUserApi>(AppUserApi);
21
+ const appUserApi = ioc.get<AppApiRequester>(AppApiRequester);
22
22
 
23
23
  appUserApi.usePlugin(new FetchURLPlugin());
24
24
  appUserApi.usePlugin(new RequestEncryptPlugin(ioc.get(StringEncryptor)));
@@ -29,11 +29,12 @@ export class AppUserApiBootstrap implements BootstrapExecutorPlugin {
29
29
  );
30
30
  appUserApi.usePlugin(new AppApiPlugin());
31
31
  appUserApi.usePlugin(ioc.get(DialogErrorPlugin));
32
+ console.log('jj AppUserApiBootstrap success');
32
33
  }
33
34
 
34
35
  protected requestDataSerializer(
35
36
  data: unknown,
36
- context: ExecutorContext<UserApiConfig>
37
+ context: ExecutorContext<AppApiConfig>
37
38
  ): unknown {
38
39
  if (data instanceof FormData) {
39
40
  return data;
@@ -1,9 +1,9 @@
1
1
  import { Bootstrap } from '@qlover/corekit-bridge';
2
2
  import { isObject } from 'lodash';
3
3
  import { browserGlobalsName } from '@config/common';
4
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
4
5
  import { BootstrapsRegistry } from './BootstrapsRegistry';
5
6
  import * as globals from '../globals';
6
- import type { IOCIdentifierMap } from '@config/IOCIdentifier';
7
7
  import type {
8
8
  IOCContainerInterface,
9
9
  IOCFunctionInterface
@@ -12,23 +12,11 @@ import {
12
12
  type PromiseTask,
13
13
  type ExecutorPlugin
14
14
  } from '@qlover/fe-corekit';
15
- import { I, type IOCIdentifierMapServer } from '@config/IOCIdentifier';
16
15
  import type { ServerInterface } from '@/server/port/ServerInterface';
16
+ import { I, type IOCIdentifierMapServer } from '@config/IOCIdentifier';
17
17
  import { ServerIOC } from '../serverIoc/ServerIOC';
18
18
 
19
- export interface BootstrapServerResult {
20
- locale: string;
21
- messages: Record<string, string>;
22
- }
23
-
24
19
  export interface BootstrapServerContextValue extends BootstrapContextValue {
25
- locale: string;
26
- messages: Record<string, string>;
27
- }
28
-
29
- interface BootstrapServerContext {
30
- logger: LoggerInterface;
31
- root: Record<string, unknown>;
32
20
  IOC: IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>;
33
21
  }
34
22
 
@@ -90,11 +78,12 @@ export class BootstrapServer implements ServerInterface {
90
78
  }
91
79
 
92
80
  execNoError<Result>(
93
- task?: PromiseTask<Result, BootstrapServerContext>
81
+ task?: PromiseTask<Result, BootstrapServerContextValue>
94
82
  ): Promise<Result | ExecutorError> {
95
83
  const context = {
96
84
  logger: this.logger,
97
85
  root: this.root,
86
+ ioc: this.IOC.implemention!,
98
87
  IOC: this.IOC
99
88
  };
100
89
 
@@ -1,9 +1,9 @@
1
- import { I, IOCIdentifier } from '@config/IOCIdentifier';
2
1
  import { AppUserApiBootstrap } from '@/base/services/appApi/AppUserApiBootstrap';
2
+ import { I, IOCIdentifier } from '@config/IOCIdentifier';
3
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
3
4
  import { IocIdentifierTest } from './IocIdentifierTest';
4
5
  import { printBootstrap } from './PrintBootstrap';
5
6
  import type { BootstrapAppArgs } from './BootstrapClient';
6
- import type { IOCIdentifierMap } from '@config/IOCIdentifier';
7
7
  import type {
8
8
  BootstrapExecutorPlugin,
9
9
  EnvConfigInterface,
@@ -1,5 +1,5 @@
1
- import { browserGlobalsName } from '@config/common';
2
1
  import { AppConfig } from '@/base/cases/AppConfig';
2
+ import { browserGlobalsName } from '@config/common';
3
3
  import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
4
4
 
5
5
  export const printBootstrap: BootstrapExecutorPlugin = {
@@ -5,9 +5,9 @@ import {
5
5
  } from '@qlover/corekit-bridge';
6
6
  import { InversifyContainer } from '@/base/cases/InversifyContainer';
7
7
  import type { IOCInterface } from '@/base/port/IOCInterface';
8
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
8
9
  import { ClientIOCRegister } from './ClientIOCRegister';
9
10
  import { appConfig } from '../globals';
10
- import type { IOCIdentifierMap } from '@config/IOCIdentifier';
11
11
 
12
12
  export class ClientIOC
13
13
  implements IOCInterface<IOCIdentifierMap, IOCContainerInterface>
@@ -1,8 +1,8 @@
1
- import { IOCIdentifier as I } from '@config/IOCIdentifier';
2
1
  import { RouterService } from '@/base/cases/RouterService';
3
2
  import type { IocRegisterOptions } from '@/base/port/IOCInterface';
4
3
  import { I18nService } from '@/base/services/I18nService';
5
4
  import { UserService } from '@/base/services/UserService';
5
+ import { IOCIdentifier as I } from '@config/IOCIdentifier';
6
6
  import { dialogHandler, logger, JSON } from '../globals';
7
7
  import type {
8
8
  IOCContainerInterface,
@@ -1,9 +1,9 @@
1
1
  // ! global variables, don't import any dependencies and don't have side effects
2
2
  import { ColorFormatter, ConsoleHandler, Logger } from '@qlover/corekit-bridge';
3
3
  import { JSONSerializer } from '@qlover/fe-corekit';
4
- import { loggerStyles } from '@config/common';
5
4
  import { AppConfig } from '@/base/cases/AppConfig';
6
5
  import { DialogHandler } from '@/base/cases/DialogHandler';
6
+ import { loggerStyles } from '@config/common';
7
7
 
8
8
  export const appConfig = new AppConfig();
9
9
 
@@ -6,8 +6,8 @@ import {
6
6
  import { AppConfig } from '@/base/cases/AppConfig';
7
7
  import { InversifyContainer } from '@/base/cases/InversifyContainer';
8
8
  import type { IOCInterface } from '@/base/port/IOCInterface';
9
- import { ServerIOCRegister } from './ServerIOCRegister';
10
9
  import type { IOCIdentifierMapServer } from '@config/IOCIdentifier';
10
+ import { ServerIOCRegister } from './ServerIOCRegister';
11
11
 
12
12
  export class ServerIOC
13
13
  implements IOCInterface<IOCIdentifierMapServer, IOCContainerInterface>
@@ -6,8 +6,8 @@ import {
6
6
  type IOCManagerInterface,
7
7
  type IOCRegisterInterface
8
8
  } from '@qlover/corekit-bridge';
9
- import { IOCIdentifier as I } from '@config/IOCIdentifier';
10
9
  import type { IocRegisterOptions } from '@/base/port/IOCInterface';
10
+ import { IOCIdentifier as I } from '@config/IOCIdentifier';
11
11
 
12
12
  export class ServerIOCRegister
13
13
  implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
@@ -1,12 +1,14 @@
1
+ import { ExecutorError } from '@qlover/fe-corekit';
1
2
  import { inject, injectable } from 'inversify';
2
3
  import { cookies } from 'next/headers';
3
- import { I } from '@config/IOCIdentifier';
4
4
  import type { AppConfig } from '@/base/cases/AppConfig';
5
+ import { API_NOT_AUTHORIZED } from '@config/Identifier';
6
+ import { I } from '@config/IOCIdentifier';
5
7
  import { UserCredentialToken } from './UserCredentialToken';
6
- import type { UserAuthInterface } from './port/UserAuthInterface';
8
+ import type { ServerAuthInterface } from './port/ServerAuthInterface';
7
9
 
8
10
  @injectable()
9
- export class ServerAuth implements UserAuthInterface {
11
+ export class ServerAuth implements ServerAuthInterface {
10
12
  protected userTokenKey: string;
11
13
  constructor(
12
14
  @inject(I.AppConfig) protected server: AppConfig,
@@ -47,4 +49,12 @@ export class ServerAuth implements UserAuthInterface {
47
49
  const cookieStore = await cookies();
48
50
  cookieStore.delete(this.userTokenKey);
49
51
  }
52
+
53
+ async throwIfNotAuth(): Promise<void> {
54
+ const hasAuth = await this.hasAuth();
55
+
56
+ if (!hasAuth) {
57
+ throw new ExecutorError(API_NOT_AUTHORIZED, 'Not authorized');
58
+ }
59
+ }
50
60
  }
@@ -1,16 +1,17 @@
1
1
  import {
2
2
  createClient,
3
3
  type PostgrestSingleResponse,
4
- type SupabaseClient
4
+ type SupabaseClient,
5
+ type PostgrestResponse
5
6
  } from '@supabase/supabase-js';
6
7
  import { injectable, inject } from 'inversify';
7
- import { I } from '@config/IOCIdentifier';
8
8
  import type { AppConfig } from '@/base/cases/AppConfig';
9
9
  import type {
10
10
  BridgeEvent,
11
11
  DBBridgeInterface,
12
12
  Where
13
13
  } from '@/base/port/DBBridgeInterface';
14
+ import { I } from '@config/IOCIdentifier';
14
15
  import type { LoggerInterface } from '@qlover/logger';
15
16
  import type { PostgrestFilterBuilder } from '@supabase/postgrest-js';
16
17
 
@@ -121,4 +122,38 @@ export class SupabaseBridge implements DBBridgeInterface {
121
122
 
122
123
  return this.catch(await handler);
123
124
  }
125
+
126
+ async pagination(event: BridgeEvent): Promise<PostgrestResponse<unknown>> {
127
+ const { table, fields = '*', where, page = 1, pageSize = 10 } = event;
128
+ const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
129
+
130
+ // 获取总数
131
+ const countHandler = this.supabase
132
+ .from(table)
133
+ .select('*', { count: 'exact', head: true });
134
+
135
+ this.handleWhere(countHandler, where ?? []);
136
+ const countResult = await this.catch(await countHandler);
137
+
138
+ // 获取分页数据
139
+ const handler = this.supabase
140
+ .from(table)
141
+ .select(selectFields)
142
+ .range((page - 1) * pageSize, page * pageSize - 1);
143
+
144
+ this.handleWhere(handler, where ?? []);
145
+ const result = await this.catch(await handler);
146
+
147
+ if (result.error) {
148
+ return result as PostgrestResponse<unknown>;
149
+ }
150
+
151
+ return {
152
+ data: Array.isArray(result.data) ? result.data : [],
153
+ error: null,
154
+ count: countResult.count,
155
+ status: result.status,
156
+ statusText: result.statusText
157
+ };
158
+ }
124
159
  }
@@ -1,8 +1,8 @@
1
1
  import { inject, injectable } from 'inversify';
2
2
  import jwt from 'jsonwebtoken';
3
3
  import { AppConfig } from '@/base/cases/AppConfig';
4
- import type { CrentialTokenInterface } from './port/CrentialTokenInterface';
5
4
  import type { UserSchema } from '@migrations/schema/UserSchema';
5
+ import type { CrentialTokenInterface } from './port/CrentialTokenInterface';
6
6
 
7
7
  export type UserCredentialTokenValue = Pick<UserSchema, 'id' | 'email'>;
8
8
 
@@ -0,0 +1,10 @@
1
+ import type { PaginationInterface } from '@/base/port/PaginationInterface';
2
+
3
+ export interface DBTableInterface {
4
+ readonly name: string;
5
+
6
+ pagination<T = unknown>(params: {
7
+ page: number;
8
+ pageSize: number;
9
+ }): Promise<PaginationInterface<T>>;
10
+ }
@@ -1,4 +1,4 @@
1
- export interface UserAuthInterface {
1
+ export interface ServerAuthInterface {
2
2
  setAuth(credential_token: string): Promise<void>;
3
3
 
4
4
  getAuth(): Promise<string>;
@@ -6,4 +6,6 @@ export interface UserAuthInterface {
6
6
  clear(): Promise<void>;
7
7
 
8
8
  hasAuth(): Promise<boolean>;
9
+
10
+ throwIfNotAuth(): Promise<void>;
9
11
  }
@@ -1,4 +1,4 @@
1
- import type { DBTableInterface } from '@/base/port/DBTableInterface';
1
+ import type { DBTableInterface } from '@/server/port/DBTableInterface';
2
2
  import type { UserSchema } from '@migrations/schema/UserSchema';
3
3
 
4
4
  export interface UserRepositoryInterface extends DBTableInterface {
@@ -1,14 +1,26 @@
1
1
  import { inject, injectable } from 'inversify';
2
2
  import { isEmpty, last } from 'lodash';
3
3
  import type { DBBridgeInterface } from '@/base/port/DBBridgeInterface';
4
+ import type { PaginationInterface } from '@/base/port/PaginationInterface';
5
+ import type { UserSchema } from '@migrations/schema/UserSchema';
4
6
  import { SupabaseBridge } from '../SupabaseBridge';
5
7
  import type { UserRepositoryInterface } from '../port/UserRepositoryInterface';
6
- import type { UserSchema } from '@migrations/schema/UserSchema';
7
8
 
8
9
  @injectable()
9
10
  export class UserRepository implements UserRepositoryInterface {
10
11
  readonly name = 'fe_users';
11
12
 
13
+ protected safeFields = [
14
+ 'created_at',
15
+ // 'credential_token',
16
+ 'email',
17
+ 'email_confirmed_at',
18
+ 'id',
19
+ // 'password',
20
+ 'role',
21
+ 'updated_at'
22
+ ];
23
+
12
24
  constructor(@inject(SupabaseBridge) protected dbBridge: DBBridgeInterface) {}
13
25
 
14
26
  getAll(): Promise<unknown> {
@@ -60,4 +72,23 @@ export class UserRepository implements UserRepositoryInterface {
60
72
  where: [['id', '=', id]]
61
73
  });
62
74
  }
75
+
76
+ async pagination<UserSchema>(params: {
77
+ page: number;
78
+ pageSize: number;
79
+ }): Promise<PaginationInterface<UserSchema>> {
80
+ const result = await this.dbBridge.pagination({
81
+ table: this.name,
82
+ page: params.page,
83
+ pageSize: params.pageSize,
84
+ fields: this.safeFields
85
+ });
86
+
87
+ return {
88
+ list: result.data as UserSchema[],
89
+ total: result.count ?? 0,
90
+ page: params.page,
91
+ pageSize: params.pageSize
92
+ };
93
+ }
63
94
  }
@@ -0,0 +1,19 @@
1
+ import type { BootstrapServerContextValue } from '@/core/bootstraps/BootstrapServer';
2
+ import { ServerAuth } from '../ServerAuth';
3
+ import type { ServerAuthInterface } from '../port/ServerAuthInterface';
4
+ import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
5
+ import type { ExecutorContext } from '@qlover/fe-corekit';
6
+
7
+ export class AdminAuthPlugin implements BootstrapExecutorPlugin {
8
+ pluginName = 'AdminAuthPlugin';
9
+
10
+ async onBefore(
11
+ context: ExecutorContext<BootstrapServerContextValue>
12
+ ): Promise<void> {
13
+ const { IOC } = context.parameters;
14
+
15
+ const serverAuth: ServerAuthInterface = IOC(ServerAuth);
16
+
17
+ await serverAuth.throwIfNotAuth();
18
+ }
19
+ }