@qlover/create-app 0.7.9 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +184 -0
- package/dist/index.cjs +3 -3
- package/dist/index.js +3 -3
- package/dist/templates/next-app/.env.template +8 -4
- package/dist/templates/next-app/config/IOCIdentifier.ts +4 -1
- package/dist/templates/next-app/config/Identifier/api.ts +34 -0
- package/dist/templates/next-app/config/Identifier/common.ts +7 -0
- package/dist/templates/next-app/config/Identifier/index.ts +2 -0
- package/dist/templates/next-app/config/Identifier/page.login.ts +2 -2
- package/dist/templates/next-app/config/Identifier/page.register.ts +43 -22
- package/dist/templates/next-app/config/Identifier/validator.ts +34 -0
- package/dist/templates/next-app/config/i18n/index.ts +1 -0
- package/dist/templates/next-app/config/i18n/register18n.ts +44 -0
- package/dist/templates/next-app/eslint.config.mjs +17 -0
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +24 -0
- package/dist/templates/next-app/migrations/sql/1694244000000.sql +10 -0
- package/dist/templates/next-app/next.config.ts +1 -0
- package/dist/templates/next-app/package.json +12 -2
- package/dist/templates/next-app/public/locales/en.json +19 -2
- package/dist/templates/next-app/public/locales/zh.json +19 -2
- package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +18 -0
- package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +22 -0
- package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +62 -0
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +26 -6
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +7 -5
- package/dist/templates/next-app/src/app/[locale]/page.tsx +5 -5
- package/dist/templates/next-app/src/app/[locale]/register/RegisterForm.tsx +176 -0
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +79 -0
- package/dist/templates/next-app/src/app/api/admin/users/route.ts +39 -0
- package/dist/templates/next-app/src/app/api/user/login/route.ts +50 -0
- package/dist/templates/next-app/src/app/api/user/logout/route.ts +27 -0
- package/dist/templates/next-app/src/app/api/user/register/route.ts +50 -0
- package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +40 -0
- package/dist/templates/next-app/src/base/cases/AppConfig.ts +19 -0
- package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +46 -0
- package/dist/templates/next-app/src/base/cases/PageParams.ts +1 -1
- package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +70 -0
- package/dist/templates/next-app/src/base/cases/RequestState.ts +20 -0
- package/dist/templates/next-app/src/base/cases/RouterService.ts +4 -0
- package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +67 -0
- package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +48 -0
- package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +26 -0
- package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +87 -0
- package/dist/templates/next-app/src/base/port/AppApiInterface.ts +14 -0
- package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +15 -0
- package/dist/templates/next-app/src/base/port/AsyncStateInterface.ts +7 -0
- package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +21 -0
- package/dist/templates/next-app/src/base/port/DBMigrationInterface.ts +92 -0
- package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +3 -2
- package/dist/templates/next-app/src/base/port/MigrationApiInterface.ts +3 -0
- package/dist/templates/next-app/src/base/port/PaginationInterface.ts +6 -0
- package/dist/templates/next-app/src/base/port/ServerApiResponseInterface.ts +6 -0
- package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +3 -2
- package/dist/templates/next-app/src/base/services/AdminUserService.ts +45 -0
- package/dist/templates/next-app/src/base/services/I18nService.ts +9 -45
- package/dist/templates/next-app/src/base/services/UserService.ts +9 -8
- package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +21 -0
- package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +34 -0
- package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +63 -0
- package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +56 -0
- package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +71 -0
- package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +49 -0
- package/dist/templates/next-app/src/base/services/migrations/MigrationsApi.ts +43 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +1 -1
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +26 -12
- package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +5 -4
- package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +1 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +1 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +1 -1
- package/dist/templates/next-app/src/core/globals.ts +1 -1
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +1 -1
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +1 -1
- package/dist/templates/next-app/src/server/AppErrorApi.ts +10 -0
- package/dist/templates/next-app/src/server/AppSuccessApi.ts +7 -0
- package/dist/templates/next-app/src/server/PasswordEncrypt.ts +12 -0
- package/dist/templates/next-app/src/server/ServerAuth.ts +60 -0
- package/dist/templates/next-app/src/server/SupabaseBridge.ts +159 -0
- package/dist/templates/next-app/src/server/UserCredentialToken.ts +49 -0
- package/dist/templates/next-app/src/server/port/CrentialTokenInterface.ts +5 -0
- package/dist/templates/next-app/src/server/port/DBTableInterface.ts +10 -0
- package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +11 -0
- package/dist/templates/next-app/src/server/port/ServerInterface.ts +22 -0
- package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +15 -0
- package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +8 -0
- package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +23 -0
- package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +94 -0
- package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +19 -0
- package/dist/templates/next-app/src/server/services/ApiUserService.ts +26 -0
- package/dist/templates/next-app/src/server/services/UserService.ts +105 -0
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +79 -0
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +48 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/{_default.css → _common/_default.css} +74 -1
- package/dist/templates/next-app/src/styles/css/antd-themes/{dark.css → _common/dark.css} +73 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/{pink.css → _common/pink.css} +70 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/index.css +4 -3
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +108 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +67 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +67 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +33 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +32 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +35 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +44 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +43 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +43 -0
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +106 -0
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +68 -17
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +6 -1
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +9 -2
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +11 -3
- package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +1 -1
- package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +21 -11
- package/dist/templates/next-app/src/uikit/hook/useIOC.ts +1 -1
- package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +7 -1
- package/dist/templates/next-app/tsconfig.json +3 -1
- package/package.json +2 -2
- package/dist/templates/next-app/src/base/cases/ServerAuth.ts +0 -17
- package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +0 -27
- package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +0 -3
- package/dist/templates/next-app/src/base/port/ServerInterface.ts +0 -12
- /package/dist/templates/next-app/src/{app/[locale]/login → uikit/components}/FeatureItem.tsx +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Base64Serializer, type Encryptor } from '@qlover/fe-corekit';
|
|
2
|
+
import { inject, injectable } from 'inversify';
|
|
3
|
+
import { I } from '@config/IOCIdentifier';
|
|
4
|
+
import type { AppConfig } from './AppConfig';
|
|
5
|
+
|
|
6
|
+
@injectable()
|
|
7
|
+
export class StringEncryptor implements Encryptor<string, string> {
|
|
8
|
+
private readonly key;
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
@inject(I.AppConfig) appConfig: AppConfig,
|
|
12
|
+
@inject(Base64Serializer) protected base64Serializer: Base64Serializer
|
|
13
|
+
) {
|
|
14
|
+
if (!appConfig.stringEncryptorKey) {
|
|
15
|
+
throw new Error('StringEncryptorKey is not set');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.key = appConfig.stringEncryptorKey;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected encryptWithKey(str: string, key: string): string {
|
|
22
|
+
const result = [];
|
|
23
|
+
for (let i = 0; i < str.length; i++) {
|
|
24
|
+
// 使用字符的 Unicode 值和密钥进行混合运算
|
|
25
|
+
const charCode = str.charCodeAt(i);
|
|
26
|
+
const keyChar = key.charCodeAt(i % key.length);
|
|
27
|
+
// 使用多个运算来增加复杂度
|
|
28
|
+
const encrypted = (charCode + keyChar * 13) ^ (keyChar + i * 7);
|
|
29
|
+
result.push(String.fromCharCode(encrypted % 65536)); // 确保结果在有效的 Unicode 范围内
|
|
30
|
+
}
|
|
31
|
+
return result.join('');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected decryptWithKey(str: string, key: string): string {
|
|
35
|
+
const result = [];
|
|
36
|
+
for (let i = 0; i < str.length; i++) {
|
|
37
|
+
const charCode = str.charCodeAt(i);
|
|
38
|
+
const keyChar = key.charCodeAt(i % key.length);
|
|
39
|
+
// 反向运算还原原始字符
|
|
40
|
+
const decrypted = (charCode ^ (keyChar + i * 7)) - keyChar * 13;
|
|
41
|
+
result.push(String.fromCharCode(decrypted));
|
|
42
|
+
}
|
|
43
|
+
return result.join('');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
encrypt(value: string): string {
|
|
47
|
+
try {
|
|
48
|
+
const encrypted = this.encryptWithKey(value, this.key);
|
|
49
|
+
return this.base64Serializer.serialize(encrypted);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Encryption failed:', error);
|
|
52
|
+
throw new Error('Encryption failed');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
decrypt(encryptedValue: string): string {
|
|
57
|
+
try {
|
|
58
|
+
// 1. 先用 base64 反序列化
|
|
59
|
+
const decoded = this.base64Serializer.deserialize(encryptedValue);
|
|
60
|
+
// 2. 然后用密钥解密
|
|
61
|
+
return this.decryptWithKey(decoded, this.key);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('Decryption failed:', error);
|
|
64
|
+
throw new Error('Decryption failed');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { inject, injectable } from 'inversify';
|
|
2
|
+
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
3
|
+
import { AppUserApi } from '../services/appApi/AppUserApi';
|
|
4
|
+
import type { AppApiSuccessInterface } from '../port/AppApiInterface';
|
|
5
|
+
import type { AppUserApiInterface } from '../port/AppUserApiInterface';
|
|
6
|
+
import type {
|
|
7
|
+
LoginResponseData,
|
|
8
|
+
UserAuthApiInterface,
|
|
9
|
+
UserAuthStoreInterface
|
|
10
|
+
} from '@qlover/corekit-bridge';
|
|
11
|
+
|
|
12
|
+
@injectable()
|
|
13
|
+
export class UserServiceApi implements UserAuthApiInterface<UserSchema> {
|
|
14
|
+
protected store: UserAuthStoreInterface<UserSchema> | null = null;
|
|
15
|
+
|
|
16
|
+
constructor(@inject(AppUserApi) protected appUserApi: AppUserApiInterface) {}
|
|
17
|
+
|
|
18
|
+
getStore(): UserAuthStoreInterface<UserSchema> | null {
|
|
19
|
+
return this.store;
|
|
20
|
+
}
|
|
21
|
+
setStore(store: UserAuthStoreInterface<UserSchema>): void {
|
|
22
|
+
this.store = store;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async login(params: {
|
|
26
|
+
email: string;
|
|
27
|
+
password: string;
|
|
28
|
+
}): Promise<LoginResponseData> {
|
|
29
|
+
const response = await this.appUserApi.login(params);
|
|
30
|
+
return (response as AppApiSuccessInterface).data as LoginResponseData;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async register(params: {
|
|
34
|
+
email: string;
|
|
35
|
+
password: string;
|
|
36
|
+
}): Promise<LoginResponseData> {
|
|
37
|
+
const response = await this.appUserApi.register(params);
|
|
38
|
+
return (response as AppApiSuccessInterface).data as LoginResponseData;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async logout(): Promise<void> {
|
|
42
|
+
await this.appUserApi.logout();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getUserInfo(loginData: LoginResponseData): Promise<UserSchema> {
|
|
46
|
+
return Promise.resolve(loginData as unknown as UserSchema);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { StoreInterface } from '@qlover/corekit-bridge';
|
|
2
|
+
import type { StoreStateInterface } from '@qlover/corekit-bridge';
|
|
3
|
+
|
|
4
|
+
export interface NavItemInterface {
|
|
5
|
+
key: string;
|
|
6
|
+
/**
|
|
7
|
+
* 可以是 i18n key
|
|
8
|
+
*/
|
|
9
|
+
i18nKey: string;
|
|
10
|
+
|
|
11
|
+
pathname?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AdminPageState extends StoreStateInterface {
|
|
15
|
+
navItems: NavItemInterface[];
|
|
16
|
+
collapsedSidebar: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export abstract class AdminLayoutInterface extends StoreInterface<AdminPageState> {
|
|
20
|
+
public readonly selectors = {
|
|
21
|
+
collapsedSidebar: (state: AdminPageState) => state.collapsedSidebar,
|
|
22
|
+
navItems: (state: AdminPageState) => state.navItems
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
abstract toggleSidebar(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { StoreInterface } from '@qlover/corekit-bridge';
|
|
2
|
+
import { RequestState } from '../cases/RequestState';
|
|
3
|
+
import type { PaginationInterface } from './PaginationInterface';
|
|
4
|
+
import type { StoreStateInterface } from '@qlover/corekit-bridge';
|
|
5
|
+
|
|
6
|
+
export interface AdminPageListParams {
|
|
7
|
+
page: number;
|
|
8
|
+
pageSize: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class AdminPageState implements StoreStateInterface {
|
|
12
|
+
listParams: AdminPageListParams = {
|
|
13
|
+
page: 1,
|
|
14
|
+
pageSize: 10
|
|
15
|
+
};
|
|
16
|
+
initState = new RequestState<unknown>();
|
|
17
|
+
listState = new RequestState<PaginationInterface<unknown>>();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export abstract class AdminPageInterface<
|
|
21
|
+
S extends AdminPageState
|
|
22
|
+
> extends StoreInterface<S> {
|
|
23
|
+
/**
|
|
24
|
+
* 初始化
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
async initialize(): Promise<unknown> {
|
|
28
|
+
this.emit(
|
|
29
|
+
this.cloneState({
|
|
30
|
+
initState: new RequestState(true)
|
|
31
|
+
} as Partial<S>)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const result = await this.fetchList(this.state.listParams);
|
|
36
|
+
|
|
37
|
+
console.log('jj result', result);
|
|
38
|
+
|
|
39
|
+
this.emit(
|
|
40
|
+
this.cloneState({
|
|
41
|
+
initState: new RequestState(false, result).end()
|
|
42
|
+
} as Partial<S>)
|
|
43
|
+
);
|
|
44
|
+
return result;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
this.emit(
|
|
47
|
+
this.cloneState({
|
|
48
|
+
initState: new RequestState(false, null, error).end()
|
|
49
|
+
} as Partial<S>)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 销毁
|
|
58
|
+
*/
|
|
59
|
+
destroy(): void {
|
|
60
|
+
this.reset();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 获取列表
|
|
65
|
+
* @param params
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
abstract fetchList(
|
|
69
|
+
params: Partial<AdminPageListParams>
|
|
70
|
+
): Promise<PaginationInterface<unknown>>;
|
|
71
|
+
|
|
72
|
+
changeListState(state: RequestState<unknown>): void {
|
|
73
|
+
this.emit(
|
|
74
|
+
this.cloneState({
|
|
75
|
+
listState: state
|
|
76
|
+
} as Partial<S>)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
changeListParams(params: Partial<AdminPageListParams>): void {
|
|
81
|
+
this.emit(
|
|
82
|
+
this.cloneState({
|
|
83
|
+
listParams: params
|
|
84
|
+
} as Partial<S>)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AppApiErrorInterface {
|
|
2
|
+
success: false;
|
|
3
|
+
id: string;
|
|
4
|
+
message?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface AppApiSuccessInterface<T = unknown> {
|
|
8
|
+
success: true;
|
|
9
|
+
data?: T;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type AppApiResult<T = unknown> =
|
|
13
|
+
| AppApiErrorInterface
|
|
14
|
+
| AppApiSuccessInterface<T>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AppApiResult } from './AppApiInterface';
|
|
2
|
+
|
|
3
|
+
export interface AppUserApiInterface {
|
|
4
|
+
login(params: {
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
}): Promise<AppApiResult<unknown>>;
|
|
8
|
+
|
|
9
|
+
register(params: {
|
|
10
|
+
email: string;
|
|
11
|
+
password: string;
|
|
12
|
+
}): Promise<AppApiResult<unknown>>;
|
|
13
|
+
|
|
14
|
+
logout(): Promise<AppApiResult<unknown>>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PostgrestSingleResponse } from '@supabase/supabase-js';
|
|
2
|
+
|
|
3
|
+
export type WhereOperation = '=' | '!=' | '>' | '<' | '>=' | '<=';
|
|
4
|
+
export type Where = [string, WhereOperation, string | number];
|
|
5
|
+
|
|
6
|
+
export interface BridgeEvent {
|
|
7
|
+
table: string;
|
|
8
|
+
fields?: string | string[];
|
|
9
|
+
where?: Where[];
|
|
10
|
+
data?: unknown;
|
|
11
|
+
page?: number;
|
|
12
|
+
pageSize?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DBBridgeInterface {
|
|
16
|
+
add(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
|
|
17
|
+
update(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
|
|
18
|
+
delete(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
|
|
19
|
+
get(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
|
|
20
|
+
pagination(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export interface MigrationConfig {
|
|
2
|
+
/**
|
|
3
|
+
* Migration version number or identifier
|
|
4
|
+
*/
|
|
5
|
+
version: string | number;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Description of what this migration does
|
|
9
|
+
*/
|
|
10
|
+
description: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Timestamp when migration was created
|
|
14
|
+
*/
|
|
15
|
+
timestamp?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MigrationResult {
|
|
19
|
+
/**
|
|
20
|
+
* Whether the migration was successful
|
|
21
|
+
*/
|
|
22
|
+
success: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Error message if migration failed
|
|
26
|
+
*/
|
|
27
|
+
error?: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Migration version that was executed
|
|
31
|
+
*/
|
|
32
|
+
version: string | number;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Time taken to execute the migration in milliseconds
|
|
36
|
+
*/
|
|
37
|
+
executionTime?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DBMigrationInterface {
|
|
41
|
+
/**
|
|
42
|
+
* Get current database version
|
|
43
|
+
*/
|
|
44
|
+
getCurrentVersion(): Promise<string | number>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get list of all applied migrations
|
|
48
|
+
*/
|
|
49
|
+
getAppliedMigrations(): Promise<MigrationConfig[]>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get list of pending migrations that need to be applied
|
|
53
|
+
*/
|
|
54
|
+
getPendingMigrations(): Promise<MigrationConfig[]>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Apply a specific migration
|
|
58
|
+
* @param migration Migration configuration
|
|
59
|
+
*/
|
|
60
|
+
applyMigration(migration: MigrationConfig): Promise<MigrationResult>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Rollback a specific migration
|
|
64
|
+
* @param migration Migration configuration
|
|
65
|
+
*/
|
|
66
|
+
rollbackMigration(migration: MigrationConfig): Promise<MigrationResult>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Apply all pending migrations
|
|
70
|
+
*/
|
|
71
|
+
migrateUp(): Promise<MigrationResult[]>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Rollback to a specific version
|
|
75
|
+
* @param targetVersion Version to rollback to
|
|
76
|
+
*/
|
|
77
|
+
migrateDown(targetVersion: string | number): Promise<MigrationResult[]>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a new migration file/record
|
|
81
|
+
* @param name Name/description of the migration
|
|
82
|
+
*/
|
|
83
|
+
createMigration(name: string): Promise<MigrationConfig>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate migration history and database state
|
|
87
|
+
*/
|
|
88
|
+
validateMigrations(): Promise<{
|
|
89
|
+
valid: boolean;
|
|
90
|
+
errors?: string[];
|
|
91
|
+
}>;
|
|
92
|
+
}
|
|
@@ -2,11 +2,12 @@ import {
|
|
|
2
2
|
StoreInterface,
|
|
3
3
|
type StoreStateInterface
|
|
4
4
|
} from '@qlover/corekit-bridge';
|
|
5
|
+
import type { Locale } from '@/i18n/routing';
|
|
5
6
|
import type { i18nConfig } from '@config/i18n';
|
|
6
7
|
|
|
7
8
|
export type SupportedLocale = (typeof i18nConfig.supportedLngs)[number];
|
|
8
9
|
export type SupportedNamespace = typeof i18nConfig.fallbackLng;
|
|
9
|
-
export type I18nServiceLocale =
|
|
10
|
+
export type I18nServiceLocale = Locale;
|
|
10
11
|
|
|
11
12
|
export class I18nServiceState implements StoreStateInterface {
|
|
12
13
|
loading: boolean = false;
|
|
@@ -16,7 +17,7 @@ export abstract class I18nServiceInterface
|
|
|
16
17
|
extends StoreInterface<I18nServiceState>
|
|
17
18
|
implements I18nServiceInterface
|
|
18
19
|
{
|
|
19
|
-
abstract t(key: string, params?: Record<string, unknown>):
|
|
20
|
+
abstract t(key: string, params?: Record<string, unknown>): string;
|
|
20
21
|
abstract changeLanguage(language: I18nServiceLocale): Promise<void>;
|
|
21
22
|
abstract changeLoading(loading: boolean): void;
|
|
22
23
|
abstract getCurrentLanguage(): Promise<I18nServiceLocale>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UserAuthService } from '@qlover/corekit-bridge';
|
|
2
|
+
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
2
3
|
import type { ExecutorPlugin } from '@qlover/fe-corekit';
|
|
3
4
|
|
|
4
5
|
export abstract class UserServiceInterface
|
|
5
|
-
extends
|
|
6
|
+
extends UserAuthService<UserSchema>
|
|
6
7
|
implements ExecutorPlugin
|
|
7
8
|
{
|
|
8
9
|
readonly pluginName = 'UserService';
|
|
@@ -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,4 +1,3 @@
|
|
|
1
|
-
import { useLocaleRoutes } from '@config/common';
|
|
2
1
|
import { i18nConfig } from '@config/i18n';
|
|
3
2
|
import {
|
|
4
3
|
I18nServiceInterface,
|
|
@@ -11,20 +10,13 @@ type TranslationFunction = ReturnType<typeof useTranslations>;
|
|
|
11
10
|
|
|
12
11
|
export class I18nService extends I18nServiceInterface {
|
|
13
12
|
readonly pluginName = 'I18nService';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
private translator: TranslationFunction | null = null;
|
|
13
|
+
protected pathname: string = '';
|
|
14
|
+
protected translator: TranslationFunction | null = null;
|
|
17
15
|
|
|
18
16
|
constructor() {
|
|
19
17
|
super(() => new I18nServiceState(i18nConfig.fallbackLng));
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
private async ensureInitialized(): Promise<void> {
|
|
23
|
-
if (!this.initialized) {
|
|
24
|
-
throw new Error('I18nService not initialized');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
20
|
setPathname(pathname: string): void {
|
|
29
21
|
this.pathname = pathname;
|
|
30
22
|
}
|
|
@@ -36,44 +28,19 @@ export class I18nService extends I18nServiceInterface {
|
|
|
36
28
|
/**
|
|
37
29
|
* @override
|
|
38
30
|
*/
|
|
39
|
-
async onBefore(): Promise<void> {
|
|
40
|
-
if (this.initialized) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.initialized = true;
|
|
45
|
-
|
|
46
|
-
// 初始化语言状态
|
|
47
|
-
const currentLang = this.getCurrentLanguageFromPath();
|
|
48
|
-
if (this.isValidLanguage(currentLang)) {
|
|
49
|
-
this.emit({ ...this.state, language: currentLang });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
private getCurrentLanguageFromPath(): string {
|
|
54
|
-
const paths = this.pathname.split('/');
|
|
55
|
-
|
|
56
|
-
for (const path of paths) {
|
|
57
|
-
if (this.isValidLanguage(path)) {
|
|
58
|
-
return path;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return i18nConfig.fallbackLng;
|
|
63
|
-
}
|
|
31
|
+
async onBefore(): Promise<void> {}
|
|
64
32
|
|
|
65
33
|
override async changeLanguage(language: I18nServiceLocale): Promise<void> {
|
|
66
34
|
try {
|
|
67
35
|
this.changeLoading(true);
|
|
68
|
-
await this.ensureInitialized();
|
|
69
36
|
|
|
70
37
|
// 在这里我们只需要更新状态,因为实际的语言切换会通过路由处理
|
|
71
38
|
this.emit({ ...this.state, language });
|
|
72
39
|
|
|
73
40
|
// 如果不使用本地化路由,则保存语言设置到本地存储
|
|
74
|
-
if (!useLocaleRoutes && typeof window !== 'undefined') {
|
|
75
|
-
|
|
76
|
-
}
|
|
41
|
+
// if (!useLocaleRoutes && typeof window !== 'undefined') {
|
|
42
|
+
// window.localStorage.setItem('i18nextLng', language);
|
|
43
|
+
// }
|
|
77
44
|
} finally {
|
|
78
45
|
this.changeLoading(false);
|
|
79
46
|
}
|
|
@@ -84,8 +51,7 @@ export class I18nService extends I18nServiceInterface {
|
|
|
84
51
|
}
|
|
85
52
|
|
|
86
53
|
override async getCurrentLanguage(): Promise<I18nServiceLocale> {
|
|
87
|
-
|
|
88
|
-
return this.getCurrentLanguageFromPath() as I18nServiceLocale;
|
|
54
|
+
return this.state.language;
|
|
89
55
|
}
|
|
90
56
|
|
|
91
57
|
override isValidLanguage(language: string): language is I18nServiceLocale {
|
|
@@ -96,12 +62,10 @@ export class I18nService extends I18nServiceInterface {
|
|
|
96
62
|
return [...i18nConfig.supportedLngs];
|
|
97
63
|
}
|
|
98
64
|
|
|
99
|
-
override
|
|
65
|
+
override t(
|
|
100
66
|
key: string,
|
|
101
67
|
params?: Record<string, string | number | Date>
|
|
102
|
-
):
|
|
103
|
-
await this.ensureInitialized();
|
|
104
|
-
|
|
68
|
+
): string {
|
|
105
69
|
if (!this.translator) {
|
|
106
70
|
return key;
|
|
107
71
|
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import { CookieStorage } from '@qlover/corekit-bridge';
|
|
2
1
|
import { injectable, inject } from 'inversify';
|
|
2
|
+
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
3
3
|
import { AppConfig } from '../cases/AppConfig';
|
|
4
|
+
import { UserServiceApi } from '../cases/UserServiceApi';
|
|
4
5
|
import { UserServiceInterface } from '../port/UserServiceInterface';
|
|
6
|
+
import type { UserAuthApiInterface } from '@qlover/corekit-bridge';
|
|
5
7
|
|
|
6
8
|
@injectable()
|
|
7
9
|
export class UserService extends UserServiceInterface {
|
|
8
|
-
constructor(
|
|
9
|
-
|
|
10
|
+
constructor(
|
|
11
|
+
@inject(AppConfig) protected appConfig: AppConfig,
|
|
12
|
+
@inject(UserServiceApi) protected userApi: UserAuthApiInterface<UserSchema>
|
|
13
|
+
) {
|
|
14
|
+
super(userApi, {
|
|
10
15
|
credentialStorage: {
|
|
11
|
-
key: appConfig.userTokenKey
|
|
12
|
-
storage: new CookieStorage()
|
|
13
|
-
},
|
|
14
|
-
requestConfig: {
|
|
15
|
-
env: appConfig.env !== 'production' ? 'development' : 'production'
|
|
16
|
+
key: appConfig.userTokenKey
|
|
16
17
|
}
|
|
17
18
|
});
|
|
18
19
|
}
|
|
@@ -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
|
+
}
|