@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.
- package/CHANGELOG.md +15 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/config/Identifier/api.ts +7 -0
- package/dist/templates/next-app/config/Identifier/pages/page.register.ts +8 -0
- package/dist/templates/next-app/config/common.ts +1 -1
- package/dist/templates/next-app/config/i18n/HomeI18n.ts +2 -0
- package/dist/templates/next-app/config/i18n/register18n.ts +2 -1
- package/dist/templates/next-app/config/route.ts +9 -0
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +1 -1
- package/dist/templates/next-app/next.config.ts +5 -4
- package/dist/templates/next-app/package.json +7 -8
- package/dist/templates/next-app/public/locales/en.json +4 -1
- package/dist/templates/next-app/public/locales/zh.json +4 -1
- package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +18 -0
- package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/LoginForm.tsx +2 -1
- package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/page.tsx +4 -5
- package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +8 -0
- package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/RegisterForm.tsx +24 -3
- package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/page.tsx +4 -5
- package/dist/templates/next-app/src/app/[locale]/page.tsx +18 -45
- package/dist/templates/next-app/src/app/api/ai/completions/route.ts +13 -13
- package/dist/templates/next-app/src/app/api/auth/callback/route.ts +11 -0
- package/dist/templates/next-app/src/app/api/callback/route.ts +49 -0
- package/dist/templates/next-app/src/base/cases/AppConfig.ts +2 -0
- package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +3 -6
- package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -1
- package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +13 -15
- package/dist/templates/next-app/src/base/cases/RouterService.ts +2 -7
- package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -6
- package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +53 -0
- package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -10
- package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -3
- package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +1 -1
- package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -18
- package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +10 -5
- package/dist/templates/next-app/src/base/services/{appApi/AppApiRequester.ts → AppApiRequester.ts} +16 -11
- package/dist/templates/next-app/src/base/services/AppUserGateway.ts +110 -0
- package/dist/templates/next-app/src/base/services/I18nService.ts +1 -7
- package/dist/templates/next-app/src/base/services/ResourceService.ts +1 -4
- package/dist/templates/next-app/src/base/services/UserService.ts +28 -17
- package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +5 -7
- package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +4 -3
- package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +24 -16
- package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +2 -5
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +15 -18
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -5
- package/dist/templates/next-app/src/core/globals.ts +1 -0
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +2 -8
- package/dist/templates/next-app/src/i18n/routing.ts +8 -3
- package/dist/templates/next-app/src/lib/supabase/client.ts +8 -0
- package/dist/templates/next-app/src/lib/supabase/conts.ts +2 -0
- package/dist/templates/next-app/src/lib/supabase/proxy.ts +84 -0
- package/dist/templates/next-app/src/lib/supabase/server.ts +38 -0
- package/dist/templates/next-app/src/proxy.ts +8 -1
- package/dist/templates/next-app/src/server/AppPageRouteParams.ts +5 -2
- package/dist/templates/next-app/src/server/NextApiServer.ts +2 -9
- package/dist/templates/next-app/src/server/PagesRouteParams.ts +3 -4
- package/dist/templates/next-app/src/server/ServerAuth.ts +18 -12
- package/dist/templates/next-app/src/server/SupabaseBridge.ts +66 -59
- package/dist/templates/next-app/src/server/controllers/UserController.ts +7 -2
- package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +4 -0
- package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -1
- package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +7 -1
- package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -3
- package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -3
- package/dist/templates/next-app/src/server/services/UserService.ts +71 -51
- package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -3
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -6
- package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +68 -0
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +3 -3
- package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -6
- package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +29 -0
- package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +21 -28
- package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +20 -0
- package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +2 -2
- package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +3 -17
- package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +8 -0
- package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +42 -0
- package/package.json +1 -1
- package/dist/templates/next-app/src/base/cases/TranslateI18nInterface.ts +0 -25
- package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +0 -78
- package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +0 -25
- package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +0 -78
- package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +0 -8
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type PostgrestSingleResponse,
|
|
4
|
-
type SupabaseClient,
|
|
5
|
-
type PostgrestResponse
|
|
6
|
-
} from '@supabase/supabase-js';
|
|
1
|
+
import { ExecutorError } from '@qlover/fe-corekit';
|
|
2
|
+
import { AuthError } from '@supabase/supabase-js';
|
|
7
3
|
import { injectable, inject } from 'inversify';
|
|
8
|
-
import
|
|
4
|
+
import { createClient } from '@/lib/supabase/server';
|
|
9
5
|
import type {
|
|
10
6
|
BridgeEvent,
|
|
11
7
|
DBBridgeInterface,
|
|
@@ -13,12 +9,18 @@ import type {
|
|
|
13
9
|
BridgeOrderBy,
|
|
14
10
|
Where
|
|
15
11
|
} from '@/server/port/DBBridgeInterface';
|
|
12
|
+
import { UserRole, UserSchema } from '@migrations/schema/UserSchema';
|
|
16
13
|
import { I } from '@config/IOCIdentifier';
|
|
17
14
|
import type { LoggerInterface } from '@qlover/logger';
|
|
15
|
+
import type { PostgrestResponseFailure } from '@supabase/postgrest-js';
|
|
18
16
|
import type {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
PostgrestSingleResponse,
|
|
18
|
+
SupabaseClient,
|
|
19
|
+
PostgrestResponse,
|
|
20
|
+
User,
|
|
21
|
+
AuthResponse,
|
|
22
|
+
UserResponse
|
|
23
|
+
} from '@supabase/supabase-js';
|
|
22
24
|
|
|
23
25
|
const whereHandlerMaps = {
|
|
24
26
|
'=': 'eq',
|
|
@@ -34,36 +36,18 @@ export type SupabaseBridgeResponse<T> = DBBridgeResponse<T> &
|
|
|
34
36
|
|
|
35
37
|
@injectable()
|
|
36
38
|
export class SupabaseBridge implements DBBridgeInterface {
|
|
37
|
-
protected
|
|
38
|
-
|
|
39
|
-
constructor(
|
|
40
|
-
@inject(I.AppConfig) appConfig: AppConfig,
|
|
41
|
-
@inject(I.Logger) protected logger: LoggerInterface
|
|
42
|
-
) {
|
|
43
|
-
this.supabase = createClient(
|
|
44
|
-
appConfig.supabaseUrl,
|
|
45
|
-
appConfig.supabaseAnonKey
|
|
46
|
-
);
|
|
47
|
-
}
|
|
39
|
+
constructor(@inject(I.Logger) protected logger: LoggerInterface) {}
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*/
|
|
52
|
-
public getSupabase(): SupabaseClient {
|
|
53
|
-
return this.supabase;
|
|
41
|
+
public async getSupabase(): Promise<SupabaseClient> {
|
|
42
|
+
return await createClient();
|
|
54
43
|
}
|
|
55
44
|
|
|
56
|
-
/**
|
|
57
|
-
* @override
|
|
58
|
-
*/
|
|
59
45
|
public async execSql(sql: string): Promise<SupabaseBridgeResponse<unknown>> {
|
|
60
|
-
const
|
|
46
|
+
const supabase = await this.getSupabase();
|
|
47
|
+
const res = await supabase.rpc('exec_sql', { sql });
|
|
61
48
|
return this.catch(res);
|
|
62
49
|
}
|
|
63
50
|
|
|
64
|
-
/**
|
|
65
|
-
* @override
|
|
66
|
-
*/
|
|
67
51
|
protected async catch(
|
|
68
52
|
result: PostgrestSingleResponse<unknown>
|
|
69
53
|
): Promise<SupabaseBridgeResponse<unknown>> {
|
|
@@ -82,23 +66,14 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
82
66
|
return result as SupabaseBridgeResponse<unknown>;
|
|
83
67
|
}
|
|
84
68
|
|
|
85
|
-
/**
|
|
86
|
-
* @override
|
|
87
|
-
*/
|
|
88
69
|
protected hasPausedProject(error: PostgrestResponseFailure): boolean {
|
|
89
70
|
return (
|
|
90
71
|
error.status === 0 && error.error.message === 'TypeError: fetch failed'
|
|
91
72
|
);
|
|
92
73
|
}
|
|
93
74
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
*/
|
|
97
|
-
protected handleWhere(
|
|
98
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
-
handler: PostgrestFilterBuilder<any, any, any, any, string, unknown, any>,
|
|
100
|
-
wheres: Where[]
|
|
101
|
-
): void {
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
protected handleWhere(handler: any, wheres: Where[]): void {
|
|
102
77
|
for (const where of wheres) {
|
|
103
78
|
const [key, operator, value] = where;
|
|
104
79
|
const opr = whereHandlerMaps[operator];
|
|
@@ -116,19 +91,41 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
116
91
|
}
|
|
117
92
|
}
|
|
118
93
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
*/
|
|
122
|
-
protected handleOrderBy(
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
-
handler: PostgrestFilterBuilder<any, any, any, any, string, unknown, any>,
|
|
125
|
-
orderBy?: BridgeOrderBy
|
|
126
|
-
): void {
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
protected handleOrderBy(handler: any, orderBy?: BridgeOrderBy): void {
|
|
127
96
|
if (orderBy) {
|
|
128
97
|
handler.order(orderBy[0], { ascending: orderBy[1] === 0 });
|
|
129
98
|
}
|
|
130
99
|
}
|
|
131
100
|
|
|
101
|
+
public toUserSchema(user: User): UserSchema {
|
|
102
|
+
return {
|
|
103
|
+
id: user.id,
|
|
104
|
+
email: user.email!,
|
|
105
|
+
created_at: user.created_at,
|
|
106
|
+
updated_at: user.updated_at,
|
|
107
|
+
role: user.role === 'admin' ? UserRole.ADMIN : UserRole.USER,
|
|
108
|
+
// 始终为空
|
|
109
|
+
password: '',
|
|
110
|
+
credential_token: ''
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public throwIfError(
|
|
115
|
+
response: AuthResponse | UserResponse | { error: unknown }
|
|
116
|
+
): void {
|
|
117
|
+
const { error } = response;
|
|
118
|
+
if (error) {
|
|
119
|
+
this.logger.info('SupabaseBridge throw error:', error);
|
|
120
|
+
|
|
121
|
+
if (error instanceof AuthError) {
|
|
122
|
+
throw new ExecutorError('SupabaseAuthError', error);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw new Error(error as string);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
132
129
|
/**
|
|
133
130
|
* @override
|
|
134
131
|
*/
|
|
@@ -137,7 +134,8 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
137
134
|
if (!data) {
|
|
138
135
|
throw new Error('Data is required for add operation');
|
|
139
136
|
}
|
|
140
|
-
const
|
|
137
|
+
const supabase = await this.getSupabase();
|
|
138
|
+
const res = await supabase
|
|
141
139
|
.from(table)
|
|
142
140
|
.insert(Array.isArray(data) ? data : [data]);
|
|
143
141
|
return this.catch(res);
|
|
@@ -152,7 +150,9 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
152
150
|
throw new Error('Data is required for upsert operation');
|
|
153
151
|
}
|
|
154
152
|
const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
|
|
155
|
-
const
|
|
153
|
+
const supabase = await this.getSupabase();
|
|
154
|
+
|
|
155
|
+
const res = await supabase
|
|
156
156
|
.from(table)
|
|
157
157
|
.upsert(Array.isArray(data) ? data : [data], {
|
|
158
158
|
onConflict: 'value'
|
|
@@ -169,8 +169,9 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
169
169
|
if (!data) {
|
|
170
170
|
throw new Error('Data is required for update operation');
|
|
171
171
|
}
|
|
172
|
+
const supabase = await this.getSupabase();
|
|
172
173
|
|
|
173
|
-
const handler =
|
|
174
|
+
const handler = supabase.from(table).update(data);
|
|
174
175
|
|
|
175
176
|
this.handleWhere(handler, where ?? []);
|
|
176
177
|
|
|
@@ -182,7 +183,9 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
182
183
|
*/
|
|
183
184
|
public async delete(event: BridgeEvent): Promise<DBBridgeResponse<unknown>> {
|
|
184
185
|
const { table, where } = event;
|
|
185
|
-
const
|
|
186
|
+
const supabase = await this.getSupabase();
|
|
187
|
+
|
|
188
|
+
const handler = supabase.from(table).delete();
|
|
186
189
|
|
|
187
190
|
this.handleWhere(handler, where ?? []);
|
|
188
191
|
|
|
@@ -197,7 +200,9 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
197
200
|
): Promise<SupabaseBridgeResponse<unknown>> {
|
|
198
201
|
const { table, fields = '*', where, orderBy } = event;
|
|
199
202
|
const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
|
|
200
|
-
const
|
|
203
|
+
const supabase = await this.getSupabase();
|
|
204
|
+
|
|
205
|
+
const handler = supabase.from(table).select(selectFields);
|
|
201
206
|
|
|
202
207
|
this.handleWhere(handler, where ?? []);
|
|
203
208
|
|
|
@@ -222,8 +227,10 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
222
227
|
} = event;
|
|
223
228
|
const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
|
|
224
229
|
|
|
230
|
+
const supabase = await this.getSupabase();
|
|
231
|
+
|
|
225
232
|
// 获取总数
|
|
226
|
-
const countHandler =
|
|
233
|
+
const countHandler = supabase
|
|
227
234
|
.from(table)
|
|
228
235
|
.select('*', { count: 'exact', head: true });
|
|
229
236
|
|
|
@@ -231,7 +238,7 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
231
238
|
const countResult = await this.catch(await countHandler);
|
|
232
239
|
|
|
233
240
|
// 获取分页数据
|
|
234
|
-
const handler =
|
|
241
|
+
const handler = supabase.from(table).select(selectFields);
|
|
235
242
|
|
|
236
243
|
this.handleOrderBy(handler, orderBy);
|
|
237
244
|
|
|
@@ -8,20 +8,25 @@ import {
|
|
|
8
8
|
LoginValidator,
|
|
9
9
|
type LoginValidatorData
|
|
10
10
|
} from '../validators/LoginValidator';
|
|
11
|
+
import {
|
|
12
|
+
SignupVerifyParamType,
|
|
13
|
+
SignupVerifyValidator
|
|
14
|
+
} from '../validators/SignupVerifyValidator';
|
|
11
15
|
import type { ServerAuthInterface } from '../port/ServerAuthInterface';
|
|
12
|
-
import type { UserControllerInerface } from '../port/UserControllerInerface';
|
|
13
16
|
import type { UserServiceInterface } from '../port/UserServiceInterface';
|
|
14
17
|
import type { ValidatorInterface } from '../port/ValidatorInterface';
|
|
15
18
|
import type { EncryptorInterface } from '@qlover/fe-corekit';
|
|
16
19
|
|
|
17
20
|
@injectable()
|
|
18
|
-
export class UserController implements
|
|
21
|
+
export class UserController implements UserServiceInterface {
|
|
19
22
|
constructor(
|
|
20
23
|
@inject(ServerAuth) protected serverAuth: ServerAuthInterface,
|
|
21
24
|
@inject(StringEncryptor)
|
|
22
25
|
protected stringEncryptor: EncryptorInterface<string, string>,
|
|
23
26
|
@inject(LoginValidator)
|
|
24
27
|
protected loginValidator: ValidatorInterface<LoginValidatorData>,
|
|
28
|
+
@inject(SignupVerifyValidator)
|
|
29
|
+
protected verifyValidator: ValidatorInterface<SignupVerifyParamType>,
|
|
25
30
|
@inject(UserService) protected userService: UserServiceInterface
|
|
26
31
|
) {}
|
|
27
32
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
2
|
+
|
|
1
3
|
export interface ServerAuthInterface {
|
|
2
4
|
setAuth(credential_token: string): Promise<void>;
|
|
3
5
|
|
|
@@ -8,4 +10,6 @@ export interface ServerAuthInterface {
|
|
|
8
10
|
hasAuth(): Promise<boolean>;
|
|
9
11
|
|
|
10
12
|
throwIfNotAuth(): Promise<void>;
|
|
13
|
+
|
|
14
|
+
getUser(): Promise<UserSchema | null>;
|
|
11
15
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ExecutorAsyncTask
|
|
1
|
+
import type { ExecutorAsyncTask } from '@qlover/fe-corekit';
|
|
2
|
+
import { type ExecutorError } from '@qlover/fe-corekit';
|
|
2
3
|
import { type IOCIdentifierMapServer } from '@config/IOCIdentifier';
|
|
3
4
|
import type {
|
|
4
5
|
ServiceIdentifier,
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
2
2
|
|
|
3
|
+
export type UserServiceRegisterParams = {
|
|
4
|
+
username?: string;
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
export interface UserServiceInterface {
|
|
4
|
-
register(params:
|
|
10
|
+
register(params: UserServiceRegisterParams): Promise<UserSchema>;
|
|
5
11
|
login(params: { email: string; password: string }): Promise<UserSchema>;
|
|
6
12
|
|
|
7
13
|
logout(): Promise<void>;
|
|
@@ -27,9 +27,6 @@ export class LocalesRepository implements LocalesRepositoryInterface {
|
|
|
27
27
|
@inject(Datetime) protected datetime: Datetime
|
|
28
28
|
) {}
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
* @override
|
|
32
|
-
*/
|
|
33
30
|
public async getAll(): Promise<LocalesSchema[]> {
|
|
34
31
|
const result = await this.dbBridge.get({
|
|
35
32
|
table: this.name,
|
|
@@ -25,9 +25,6 @@ export class UserRepository implements UserRepositoryInterface {
|
|
|
25
25
|
@inject(I.DBBridgeInterface) protected dbBridge: DBBridgeInterface
|
|
26
26
|
) {}
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* @override
|
|
30
|
-
*/
|
|
31
28
|
public getAll(): Promise<unknown> {
|
|
32
29
|
return this.dbBridge.get({ table: this.name });
|
|
33
30
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { ExecutorError, type EncryptorInterface } from '@qlover/fe-corekit';
|
|
2
|
+
import { Session, User } from '@supabase/supabase-js';
|
|
1
3
|
import { inject, injectable } from 'inversify';
|
|
2
|
-
import {
|
|
4
|
+
import { isString } from 'lodash';
|
|
5
|
+
import { AppConfig } from '@/base/cases/AppConfig';
|
|
3
6
|
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
API_USER_ALREADY_EXISTS
|
|
7
|
-
} from '@config/Identifier/api';
|
|
7
|
+
import { API_USER_NOT_FOUND } from '@config/Identifier/api';
|
|
8
|
+
import { I } from '@config/IOCIdentifier';
|
|
8
9
|
import { PasswordEncrypt } from '../PasswordEncrypt';
|
|
9
10
|
import { UserRepository } from '../repositorys/UserRepository';
|
|
10
11
|
import { ServerAuth } from '../ServerAuth';
|
|
12
|
+
import { SupabaseBridge } from '../SupabaseBridge';
|
|
11
13
|
import {
|
|
12
14
|
UserCredentialToken,
|
|
13
15
|
type UserCredentialTokenValue
|
|
@@ -15,11 +17,20 @@ import {
|
|
|
15
17
|
import type { CrentialTokenInterface } from '../port/CrentialTokenInterface';
|
|
16
18
|
import type { ServerAuthInterface } from '../port/ServerAuthInterface';
|
|
17
19
|
import type { UserRepositoryInterface } from '../port/UserRepositoryInterface';
|
|
18
|
-
import type {
|
|
19
|
-
|
|
20
|
+
import type {
|
|
21
|
+
UserServiceInterface,
|
|
22
|
+
UserServiceRegisterParams
|
|
23
|
+
} from '../port/UserServiceInterface';
|
|
24
|
+
import type { LoggerInterface } from '@qlover/logger';
|
|
20
25
|
|
|
21
26
|
@injectable()
|
|
22
27
|
export class UserService implements UserServiceInterface {
|
|
28
|
+
@inject(I.Logger)
|
|
29
|
+
protected logger!: LoggerInterface;
|
|
30
|
+
|
|
31
|
+
@inject(I.AppConfig)
|
|
32
|
+
protected appConfig!: AppConfig;
|
|
33
|
+
|
|
23
34
|
constructor(
|
|
24
35
|
@inject(UserRepository)
|
|
25
36
|
protected userRepository: UserRepositoryInterface,
|
|
@@ -28,33 +39,39 @@ export class UserService implements UserServiceInterface {
|
|
|
28
39
|
@inject(PasswordEncrypt)
|
|
29
40
|
protected encryptor: EncryptorInterface<string, string>,
|
|
30
41
|
@inject(UserCredentialToken)
|
|
31
|
-
protected credentialToken: CrentialTokenInterface<UserCredentialTokenValue
|
|
42
|
+
protected credentialToken: CrentialTokenInterface<UserCredentialTokenValue>,
|
|
43
|
+
@inject(SupabaseBridge) protected supabaseBridge: SupabaseBridge
|
|
32
44
|
) {}
|
|
33
45
|
|
|
34
46
|
/**
|
|
35
47
|
* @override
|
|
36
48
|
*/
|
|
37
|
-
public async register(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
public async register(
|
|
50
|
+
params: UserServiceRegisterParams
|
|
51
|
+
): Promise<UserSchema> {
|
|
52
|
+
const supabase = await this.supabaseBridge.getSupabase();
|
|
53
|
+
|
|
54
|
+
// TODO: 检查 username, 是否重复
|
|
55
|
+
// const user = await this.userRepository.getUserByEmail(params.email);
|
|
56
|
+
// if (!isEmpty(user)) {
|
|
57
|
+
// throw new Error(API_USER_ALREADY_EXISTS);
|
|
58
|
+
// }
|
|
59
|
+
|
|
60
|
+
const result = await supabase.auth.signUp({
|
|
48
61
|
email: params.email,
|
|
49
|
-
password:
|
|
62
|
+
password: params.password
|
|
63
|
+
|
|
64
|
+
// options: {
|
|
65
|
+
// emailRedirectTo: 'http://localhost:3100/callback'
|
|
66
|
+
// }
|
|
50
67
|
});
|
|
68
|
+
this.supabaseBridge.throwIfError(result);
|
|
51
69
|
|
|
52
|
-
|
|
53
|
-
if (!target) {
|
|
70
|
+
if (!result.data.user) {
|
|
54
71
|
throw new Error(API_USER_NOT_FOUND);
|
|
55
72
|
}
|
|
56
73
|
|
|
57
|
-
return
|
|
74
|
+
return this.supabaseBridge.toUserSchema(result.data.user);
|
|
58
75
|
}
|
|
59
76
|
|
|
60
77
|
/**
|
|
@@ -63,52 +80,55 @@ export class UserService implements UserServiceInterface {
|
|
|
63
80
|
public async login(params: {
|
|
64
81
|
email: string;
|
|
65
82
|
password: string;
|
|
83
|
+
authCode?: string;
|
|
66
84
|
}): Promise<UserSchema> {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
if (!user) {
|
|
70
|
-
throw new Error(API_USER_NOT_FOUND);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const encryptedPassword = this.encryptor.encrypt(params.password);
|
|
85
|
+
const supabase = await this.supabaseBridge.getSupabase();
|
|
74
86
|
|
|
75
|
-
if (
|
|
76
|
-
|
|
87
|
+
if (params.authCode) {
|
|
88
|
+
const ares = await supabase.auth.exchangeCodeForSession(params.authCode);
|
|
89
|
+
this.supabaseBridge.throwIfError(ares);
|
|
77
90
|
}
|
|
78
91
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
credential_token: credentialToken
|
|
92
|
+
const result = await supabase.auth.signInWithPassword({
|
|
93
|
+
email: params.email,
|
|
94
|
+
password: params.password
|
|
83
95
|
});
|
|
96
|
+
this.supabaseBridge.throwIfError(result);
|
|
84
97
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
this.logger.info('supbase login succees', result.data);
|
|
99
|
+
|
|
100
|
+
return this.supabaseBridge.toUserSchema(result.data.user!);
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
/**
|
|
91
104
|
* @override
|
|
92
105
|
*/
|
|
93
106
|
public async logout(): Promise<void> {
|
|
94
|
-
const
|
|
107
|
+
const supabase = await this.supabaseBridge.getSupabase();
|
|
108
|
+
|
|
109
|
+
const response = await supabase.auth.signOut();
|
|
110
|
+
|
|
111
|
+
this.supabaseBridge.throwIfError(response);
|
|
112
|
+
}
|
|
95
113
|
|
|
96
|
-
|
|
97
|
-
|
|
114
|
+
public async exchangeSessionForCode(code: string): Promise<{
|
|
115
|
+
user: User;
|
|
116
|
+
session: Session;
|
|
117
|
+
}> {
|
|
118
|
+
if (code == null || !isString(code)) {
|
|
119
|
+
throw new ExecutorError('code is required');
|
|
98
120
|
}
|
|
99
121
|
|
|
100
|
-
|
|
101
|
-
|
|
122
|
+
const supabase = await this.supabaseBridge.getSupabase();
|
|
123
|
+
const response = await supabase.auth.exchangeCodeForSession(code);
|
|
124
|
+
this.supabaseBridge.throwIfError(response);
|
|
102
125
|
|
|
103
|
-
|
|
126
|
+
this.logger.debug('exchangeSessionForCode', response.data);
|
|
104
127
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
});
|
|
108
|
-
} catch {
|
|
109
|
-
return;
|
|
128
|
+
if (!response.data.user) {
|
|
129
|
+
throw new ExecutorError(API_USER_NOT_FOUND);
|
|
110
130
|
}
|
|
111
131
|
|
|
112
|
-
|
|
132
|
+
return response.data;
|
|
113
133
|
}
|
|
114
134
|
}
|
|
@@ -53,9 +53,6 @@ export class LocalesValidator implements ValidatorInterface<
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export class LocalesImportValidator implements ValidatorInterface<ImportLocalesData> {
|
|
56
|
-
/**
|
|
57
|
-
* @override
|
|
58
|
-
*/
|
|
59
56
|
public getHasAnyFilesLocale(
|
|
60
57
|
values: FormData
|
|
61
58
|
): { language: LocaleType; value: FormDataEntryValue }[] {
|
|
@@ -27,9 +27,6 @@ const passwordSchema = z
|
|
|
27
27
|
.regex(/^\S+$/, { message: V_PASSWORD_SPECIAL_CHARS });
|
|
28
28
|
|
|
29
29
|
export class LoginValidator implements ValidatorInterface<LoginValidatorData> {
|
|
30
|
-
/**
|
|
31
|
-
* @override
|
|
32
|
-
*/
|
|
33
30
|
public validateEmail(data: unknown): void | ValidationFaildResult {
|
|
34
31
|
const emailResult = emailSchema.safeParse(data);
|
|
35
32
|
if (!emailResult.success) {
|
|
@@ -37,9 +34,6 @@ export class LoginValidator implements ValidatorInterface<LoginValidatorData> {
|
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
/**
|
|
41
|
-
* @override
|
|
42
|
-
*/
|
|
43
37
|
public validatePassword(data: unknown): void | ValidationFaildResult {
|
|
44
38
|
const passwordResult = passwordSchema.safeParse(data);
|
|
45
39
|
if (!passwordResult.success) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ExecutorError } from '@qlover/fe-corekit';
|
|
2
|
+
import { isPlainObject, isString } from 'lodash';
|
|
3
|
+
import type { ExtendedExecutorError } from './ExtendedExecutorError';
|
|
4
|
+
import type {
|
|
5
|
+
ValidationFaildResult,
|
|
6
|
+
ValidatorInterface
|
|
7
|
+
} from '../port/ValidatorInterface';
|
|
8
|
+
import type { EmailOtpType } from '@supabase/supabase-js';
|
|
9
|
+
|
|
10
|
+
export type SignupVerifyParamType = {
|
|
11
|
+
access_token: string;
|
|
12
|
+
expires_at: string;
|
|
13
|
+
expires_in: string;
|
|
14
|
+
refresh_token: string;
|
|
15
|
+
token_type: string;
|
|
16
|
+
type: EmailOtpType;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const emailVerifyParamKeys = [
|
|
20
|
+
'access_token',
|
|
21
|
+
'expires_at',
|
|
22
|
+
'expires_in',
|
|
23
|
+
'refresh_token',
|
|
24
|
+
'token_type',
|
|
25
|
+
'type'
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
export class SignupVerifyValidator implements ValidatorInterface<SignupVerifyParamType> {
|
|
29
|
+
/**
|
|
30
|
+
* @override
|
|
31
|
+
*/
|
|
32
|
+
public validate(data: unknown): void | ValidationFaildResult {
|
|
33
|
+
if (!isPlainObject(data)) {
|
|
34
|
+
return {
|
|
35
|
+
path: ['form'],
|
|
36
|
+
message: 'Invalid Signup verify params'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const key of emailVerifyParamKeys) {
|
|
41
|
+
if (
|
|
42
|
+
!(
|
|
43
|
+
isString((data as SignupVerifyParamType)[key]) &&
|
|
44
|
+
(data as SignupVerifyParamType)[key]
|
|
45
|
+
)
|
|
46
|
+
) {
|
|
47
|
+
return {
|
|
48
|
+
path: [key],
|
|
49
|
+
message: `Invalid Signup verify ${key} params`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* @override
|
|
56
|
+
*/
|
|
57
|
+
public getThrow(data: unknown): SignupVerifyParamType {
|
|
58
|
+
const result = this.validate(data);
|
|
59
|
+
|
|
60
|
+
if (result == null) {
|
|
61
|
+
return data as SignupVerifyParamType;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const error: ExtendedExecutorError = new ExecutorError(result.message);
|
|
65
|
+
error.issues = [result];
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import '@ant-design/v5-patch-for-react-19';
|
|
3
3
|
|
|
4
|
-
import { useEffect, useState } from 'react';
|
|
5
4
|
import { useLocale } from 'next-intl';
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
import type { I18nServiceLocale } from '@/base/port/I18nServiceInterface';
|
|
6
7
|
import { BootstrapClient } from '@/core/bootstraps/BootstrapClient';
|
|
8
|
+
import { I } from '@config/IOCIdentifier';
|
|
7
9
|
import { useIOC } from '../hook/useIOC';
|
|
8
10
|
import { useStrictEffect } from '../hook/useStrictEffect';
|
|
9
11
|
import { useWarnTranslations } from '../hook/useWarnTranslations';
|
|
10
|
-
import { I } from '@config/IOCIdentifier';
|
|
11
|
-
import type { I18nServiceLocale } from '@/base/port/I18nServiceInterface';
|
|
12
12
|
|
|
13
13
|
export function BootstrapsProvider(props: { children: React.ReactNode }) {
|
|
14
14
|
const IOC = useIOC();
|
package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts
CHANGED
|
@@ -10,18 +10,12 @@ export class LocalesImportEvent extends StoreInterface<LocalesImportEventState>
|
|
|
10
10
|
super(() => new LocalesImportEventState());
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* @override
|
|
15
|
-
*/
|
|
16
13
|
protected validate(file: File): void {
|
|
17
14
|
if (file.type !== 'application/json') {
|
|
18
15
|
throw new Error('File must be a JSON file');
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
/**
|
|
23
|
-
* @override
|
|
24
|
-
*/
|
|
25
19
|
public async onImport(type: LocaleType, file: File): Promise<void> {
|
|
26
20
|
try {
|
|
27
21
|
this.validate(file);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { TeamOutlined } from '@ant-design/icons';
|
|
2
|
+
import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
3
|
+
import { ServerAuth } from '@/server/ServerAuth';
|
|
4
|
+
import { LocaleLink } from '../components/LocaleLink';
|
|
5
|
+
|
|
6
|
+
export async function AdminButton(props: {
|
|
7
|
+
adminTitle: string;
|
|
8
|
+
locale?: string;
|
|
9
|
+
}) {
|
|
10
|
+
const { adminTitle, locale } = props;
|
|
11
|
+
const hasAuth = await bootstrapServer.getIOC(ServerAuth).hasAuth();
|
|
12
|
+
|
|
13
|
+
if (!hasAuth) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<LocaleLink
|
|
19
|
+
data-testid="AdminButton"
|
|
20
|
+
key="admin-button"
|
|
21
|
+
href="/admin"
|
|
22
|
+
title={adminTitle}
|
|
23
|
+
locale={locale}
|
|
24
|
+
className="text-text hover:text-text-hover cursor-pointer text-lg transition-colors"
|
|
25
|
+
>
|
|
26
|
+
<TeamOutlined className="text-lg text-text" />
|
|
27
|
+
</LocaleLink>
|
|
28
|
+
);
|
|
29
|
+
}
|