@qlover/create-app 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/index.cjs +9 -9
- package/dist/index.js +9 -9
- 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/dist/templates/react-app/config/i18n/PageI18nInterface.ts +2 -0
- package/package.json +2 -2
- 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
|
@@ -23,7 +23,6 @@ export class ClientIOCRegister implements IOCRegisterInterface<
|
|
|
23
23
|
*
|
|
24
24
|
* 一般用于注册全局
|
|
25
25
|
*
|
|
26
|
-
* @override
|
|
27
26
|
* @param ioc - IOC container
|
|
28
27
|
*/
|
|
29
28
|
protected registerGlobals(ioc: IOCContainerInterface): void {
|
|
@@ -48,7 +47,6 @@ export class ClientIOCRegister implements IOCRegisterInterface<
|
|
|
48
47
|
* - 国际化服务
|
|
49
48
|
* - 主题服务
|
|
50
49
|
*
|
|
51
|
-
* @override
|
|
52
50
|
* @param ioc
|
|
53
51
|
*/
|
|
54
52
|
protected registerImplement(ioc: IOCContainerInterface): void {
|
|
@@ -70,9 +68,6 @@ export class ClientIOCRegister implements IOCRegisterInterface<
|
|
|
70
68
|
// ioc.bind(I.RequestPageBridgeInterface, ioc.get(RequestPageBridge));
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
/**
|
|
74
|
-
* @override
|
|
75
|
-
*/
|
|
76
71
|
protected registerCommon(_ioc: IOCContainerInterface): void {
|
|
77
72
|
// const { appConfig } = this.options;
|
|
78
73
|
// const logger = ioc.get(I.Logger);
|
|
@@ -19,7 +19,6 @@ export class ServerIOCRegister implements IOCRegisterInterface<
|
|
|
19
19
|
*
|
|
20
20
|
* 一般用于注册全局
|
|
21
21
|
*
|
|
22
|
-
* @override
|
|
23
22
|
* @param ioc - IOC container
|
|
24
23
|
*/
|
|
25
24
|
protected registerGlobals(ioc: IOCContainerInterface): void {
|
|
@@ -28,6 +27,7 @@ export class ServerIOCRegister implements IOCRegisterInterface<
|
|
|
28
27
|
ioc.bind(
|
|
29
28
|
I.Logger,
|
|
30
29
|
new Logger({
|
|
30
|
+
name: 'next-app-server',
|
|
31
31
|
handlers: new ConsoleHandler(
|
|
32
32
|
new TimestampFormatter({
|
|
33
33
|
localeOptions: {
|
|
@@ -41,21 +41,15 @@ export class ServerIOCRegister implements IOCRegisterInterface<
|
|
|
41
41
|
})
|
|
42
42
|
),
|
|
43
43
|
silent: false,
|
|
44
|
-
level: appConfig.
|
|
44
|
+
level: appConfig.isProduction ? 'warn' : 'debug'
|
|
45
45
|
})
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/**
|
|
50
|
-
* @override
|
|
51
|
-
*/
|
|
52
49
|
protected registerImplement(ioc: IOCContainerInterface): void {
|
|
53
50
|
ioc.bind(I.DBBridgeInterface, ioc.get(SupabaseBridge));
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
/**
|
|
57
|
-
* @override
|
|
58
|
-
*/
|
|
59
53
|
protected registerCommon(_ioc: IOCContainerInterface): void {}
|
|
60
54
|
|
|
61
55
|
/**
|
|
@@ -2,6 +2,7 @@ import { createNavigation } from 'next-intl/navigation';
|
|
|
2
2
|
import { defineRouting } from 'next-intl/routing';
|
|
3
3
|
import { useLocaleRoutes } from '@config/common';
|
|
4
4
|
import { i18nConfig } from '@config/i18n';
|
|
5
|
+
import { ROUTE_LOGIN, ROUTE_REGISTER } from '@config/route';
|
|
5
6
|
|
|
6
7
|
const locales = i18nConfig.supportedLngs;
|
|
7
8
|
|
|
@@ -19,9 +20,13 @@ export const routing = defineRouting({
|
|
|
19
20
|
en: '/',
|
|
20
21
|
zh: '/'
|
|
21
22
|
},
|
|
22
|
-
|
|
23
|
-
en: '/login',
|
|
24
|
-
zh: '/login'
|
|
23
|
+
[ROUTE_LOGIN]: {
|
|
24
|
+
en: '/auth/login',
|
|
25
|
+
zh: '/auth/login'
|
|
26
|
+
},
|
|
27
|
+
[ROUTE_REGISTER]: {
|
|
28
|
+
en: '/auth/register',
|
|
29
|
+
zh: '/auth/register'
|
|
25
30
|
}
|
|
26
31
|
}
|
|
27
32
|
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createServerClient } from '@supabase/ssr';
|
|
2
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
3
|
+
import { ROUTE_LOGIN, ROUTE_REGISTER } from '@config/route';
|
|
4
|
+
import { SUPABASE_KEY, SUPABASE_URL } from './conts';
|
|
5
|
+
|
|
6
|
+
export async function updateSession(request: NextRequest) {
|
|
7
|
+
let supabaseResponse = NextResponse.next({
|
|
8
|
+
request
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// If the env vars are not set, skip proxy check. You can remove this
|
|
12
|
+
// once you setup the project.
|
|
13
|
+
if (!SUPABASE_URL && SUPABASE_KEY) {
|
|
14
|
+
return supabaseResponse;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// With Fluid compute, don't put this client in a global environment
|
|
18
|
+
// variable. Always create a new one on each request.
|
|
19
|
+
const supabase = createServerClient(SUPABASE_URL!, SUPABASE_KEY!, {
|
|
20
|
+
// global: {
|
|
21
|
+
// fetch: (input, init) => {
|
|
22
|
+
// console.log('proxy supabase globals fetch', input, init);
|
|
23
|
+
|
|
24
|
+
// return fetch(input, init);
|
|
25
|
+
// }
|
|
26
|
+
// },
|
|
27
|
+
cookies: {
|
|
28
|
+
getAll() {
|
|
29
|
+
return request.cookies.getAll();
|
|
30
|
+
},
|
|
31
|
+
setAll(cookiesToSet) {
|
|
32
|
+
cookiesToSet.forEach(({ name, value }) =>
|
|
33
|
+
request.cookies.set(name, value)
|
|
34
|
+
);
|
|
35
|
+
supabaseResponse = NextResponse.next({
|
|
36
|
+
request
|
|
37
|
+
});
|
|
38
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
39
|
+
supabaseResponse.cookies.set(name, value, options)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Do not run code between createServerClient and
|
|
46
|
+
// supabase.auth.getClaims(). A simple mistake could make it very hard to debug
|
|
47
|
+
// issues with users being randomly logged out.
|
|
48
|
+
|
|
49
|
+
// IMPORTANT: If you remove getClaims() and you use server-side rendering
|
|
50
|
+
// with the Supabase client, your users may be randomly logged out.
|
|
51
|
+
const { data } = await supabase.auth.getClaims();
|
|
52
|
+
const user = data?.claims;
|
|
53
|
+
|
|
54
|
+
console.log('Proxy(supabase) logged?:', !!user);
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
request.nextUrl.pathname !== '/' &&
|
|
58
|
+
!user &&
|
|
59
|
+
!request.nextUrl.pathname.includes(ROUTE_LOGIN) &&
|
|
60
|
+
!request.nextUrl.pathname.includes(ROUTE_REGISTER)
|
|
61
|
+
) {
|
|
62
|
+
console.log('proxy supabase no user to login');
|
|
63
|
+
|
|
64
|
+
// no user, potentially respond by redirecting the user to the login page
|
|
65
|
+
const url = request.nextUrl.clone();
|
|
66
|
+
url.pathname = ROUTE_LOGIN;
|
|
67
|
+
return NextResponse.redirect(url);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// IMPORTANT: You *must* return the supabaseResponse object as it is.
|
|
71
|
+
// If you're creating a new response object with NextResponse.next() make sure to:
|
|
72
|
+
// 1. Pass the request in it, like so:
|
|
73
|
+
// const myNewResponse = NextResponse.next({ request })
|
|
74
|
+
// 2. Copy over the cookies, like so:
|
|
75
|
+
// myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
|
|
76
|
+
// 3. Change the myNewResponse object to fit your needs, but avoid changing
|
|
77
|
+
// the cookies!
|
|
78
|
+
// 4. Finally:
|
|
79
|
+
// return myNewResponse
|
|
80
|
+
// If this is not done, you may be causing the browser and server to go out
|
|
81
|
+
// of sync and terminate the user's session prematurely!
|
|
82
|
+
|
|
83
|
+
return supabaseResponse;
|
|
84
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createServerClient } from '@supabase/ssr';
|
|
2
|
+
import { cookies } from 'next/headers';
|
|
3
|
+
import { SUPABASE_KEY, SUPABASE_URL } from './conts';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Especially important if using Fluid compute: Don't put this client in a
|
|
7
|
+
* global variable. Always create a new client within each function when using
|
|
8
|
+
* it.
|
|
9
|
+
*/
|
|
10
|
+
export async function createClient() {
|
|
11
|
+
const cookieStore = await cookies();
|
|
12
|
+
|
|
13
|
+
return createServerClient(SUPABASE_URL!, SUPABASE_KEY!, {
|
|
14
|
+
// global: {
|
|
15
|
+
// fetch: (input, init) => {
|
|
16
|
+
// console.log('supabase globals fetch', input, init);
|
|
17
|
+
|
|
18
|
+
// return fetch(input, init);
|
|
19
|
+
// }
|
|
20
|
+
// },
|
|
21
|
+
cookies: {
|
|
22
|
+
getAll() {
|
|
23
|
+
return cookieStore.getAll();
|
|
24
|
+
},
|
|
25
|
+
setAll(cookiesToSet) {
|
|
26
|
+
try {
|
|
27
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
28
|
+
cookieStore.set(name, value, options)
|
|
29
|
+
);
|
|
30
|
+
} catch {
|
|
31
|
+
// The `setAll` method was called from a Server Component.
|
|
32
|
+
// This can be ignored if you have proxy refreshing
|
|
33
|
+
// user sessions.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -5,10 +5,17 @@ import createMiddleware from 'next-intl/middleware';
|
|
|
5
5
|
|
|
6
6
|
// Import your routing configuration which contains all locales, defaultLocale, and pathnames
|
|
7
7
|
import { routing } from './i18n/routing';
|
|
8
|
+
import { updateSession } from './lib/supabase/proxy';
|
|
9
|
+
import type { NextRequest } from 'next/server';
|
|
8
10
|
|
|
9
11
|
// Export the middleware created by next-intl
|
|
10
12
|
// This middleware will handle locale detection, redirects, and internationalized routing automatically
|
|
11
|
-
export default createMiddleware(routing);
|
|
13
|
+
// export default createMiddleware(routing);
|
|
14
|
+
|
|
15
|
+
export default async function proxy(request: NextRequest) {
|
|
16
|
+
await updateSession(request);
|
|
17
|
+
return createMiddleware(routing)(request);
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
// Next.js middleware configuration object
|
|
14
21
|
export const config = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cookies } from 'next/headers';
|
|
2
2
|
import { notFound } from 'next/navigation';
|
|
3
3
|
import { getMessages, getTranslations } from 'next-intl/server';
|
|
4
|
-
import {
|
|
4
|
+
import { TranslateI18nUtil } from '@/base/cases/TranslateI18nUtil';
|
|
5
5
|
import { filterMessagesByNamespace } from '@/i18n/loadMessages';
|
|
6
6
|
import { i18nConfig } from '@config/i18n';
|
|
7
7
|
import type { LocaleType, PageI18nInterface } from '@config/i18n';
|
|
@@ -91,7 +91,10 @@ export class AppPageRouteParams<
|
|
|
91
91
|
// namespace: namespace
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
return
|
|
94
|
+
return TranslateI18nUtil.translate<T>(
|
|
95
|
+
i18nInterface,
|
|
96
|
+
TranslateI18nUtil.overrideTranslateT(t)
|
|
97
|
+
);
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
/**
|
|
@@ -12,16 +12,10 @@ import { AppSuccessApi } from './AppSuccessApi';
|
|
|
12
12
|
import type { ExecutorAsyncTask } from '@qlover/fe-corekit';
|
|
13
13
|
|
|
14
14
|
export class NextApiServer extends BootstrapServer {
|
|
15
|
-
/**
|
|
16
|
-
* @override
|
|
17
|
-
*/
|
|
18
15
|
protected isAppApiResult(result: unknown): result is AppApiResult {
|
|
19
16
|
return isAppApiSuccessInterface(result) || isAppApiErrorInterface(result);
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
/**
|
|
23
|
-
* @override
|
|
24
|
-
*/
|
|
25
19
|
public async run<Result>(
|
|
26
20
|
task?: ExecutorAsyncTask<
|
|
27
21
|
Result | AppApiResult,
|
|
@@ -37,20 +31,19 @@ export class NextApiServer extends BootstrapServer {
|
|
|
37
31
|
|
|
38
32
|
// If result is ExecutorError, return AppErrorApi
|
|
39
33
|
if (result instanceof ExecutorError) {
|
|
34
|
+
this.logger.debug('NextApiServer run error:', result);
|
|
40
35
|
return new AppErrorApi(result.id, result.message);
|
|
41
36
|
}
|
|
42
37
|
|
|
43
38
|
// If result is Error, return AppErrorApi
|
|
44
39
|
if (result instanceof Error) {
|
|
40
|
+
this.logger.debug('NextApiServer run error:', result);
|
|
45
41
|
return new AppErrorApi('SERVER_ERROR', result.message);
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
return new AppSuccessApi(result);
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
/**
|
|
52
|
-
* @override
|
|
53
|
-
*/
|
|
54
47
|
public async runWithJson<Result>(
|
|
55
48
|
task?: ExecutorAsyncTask<
|
|
56
49
|
Result | AppApiResult,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TranslateI18nUtil } from '@/base/cases/TranslateI18nUtil';
|
|
2
2
|
import { loadMessages } from '@/i18n/loadMessages';
|
|
3
3
|
import { i18nConfig } from '@config/i18n';
|
|
4
4
|
import type { LocaleType, PageI18nInterface } from '@config/i18n';
|
|
@@ -100,9 +100,8 @@ export class PagesRouteParams implements RouteParamsnHandlerInterface {
|
|
|
100
100
|
/**
|
|
101
101
|
* 获取翻译后的 i18n 接口
|
|
102
102
|
* 创建一个简单的翻译函数,基于加载的 messages
|
|
103
|
-
|
|
104
103
|
* @override
|
|
105
|
-
|
|
104
|
+
*/
|
|
106
105
|
public async getI18nInterface<T extends PageI18nInterface>(
|
|
107
106
|
i18nInterface: T,
|
|
108
107
|
namespace?: string
|
|
@@ -131,7 +130,7 @@ export class PagesRouteParams implements RouteParamsnHandlerInterface {
|
|
|
131
130
|
return message;
|
|
132
131
|
};
|
|
133
132
|
|
|
134
|
-
return
|
|
133
|
+
return TranslateI18nUtil.translate<T>(
|
|
135
134
|
i18nInterface,
|
|
136
135
|
t as ReturnType<typeof useTranslations>
|
|
137
136
|
);
|
|
@@ -2,8 +2,10 @@ import { ExecutorError } from '@qlover/fe-corekit';
|
|
|
2
2
|
import { inject, injectable } from 'inversify';
|
|
3
3
|
import { cookies } from 'next/headers';
|
|
4
4
|
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
5
|
+
import { UserSchema } from '@migrations/schema/UserSchema';
|
|
5
6
|
import { API_NOT_AUTHORIZED } from '@config/Identifier';
|
|
6
7
|
import { I } from '@config/IOCIdentifier';
|
|
8
|
+
import { SupabaseBridge } from './SupabaseBridge';
|
|
7
9
|
import { UserCredentialToken } from './UserCredentialToken';
|
|
8
10
|
import type { ServerAuthInterface } from './port/ServerAuthInterface';
|
|
9
11
|
|
|
@@ -13,7 +15,8 @@ export class ServerAuth implements ServerAuthInterface {
|
|
|
13
15
|
constructor(
|
|
14
16
|
@inject(I.AppConfig) protected server: AppConfig,
|
|
15
17
|
@inject(UserCredentialToken)
|
|
16
|
-
protected userCredentialToken: UserCredentialToken
|
|
18
|
+
protected userCredentialToken: UserCredentialToken,
|
|
19
|
+
@inject(SupabaseBridge) protected supabase: SupabaseBridge
|
|
17
20
|
) {
|
|
18
21
|
this.userTokenKey = server.userTokenKey;
|
|
19
22
|
}
|
|
@@ -31,19 +34,11 @@ export class ServerAuth implements ServerAuthInterface {
|
|
|
31
34
|
* @override
|
|
32
35
|
*/
|
|
33
36
|
public async hasAuth(): Promise<boolean> {
|
|
34
|
-
const
|
|
37
|
+
const supabase = await this.supabase.getSupabase();
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const user = await this.userCredentialToken.parseToken(token);
|
|
39
|
+
const { data } = await supabase.auth.getClaims();
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
} catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
41
|
+
return !!data?.claims;
|
|
47
42
|
}
|
|
48
43
|
|
|
49
44
|
/**
|
|
@@ -72,4 +67,15 @@ export class ServerAuth implements ServerAuthInterface {
|
|
|
72
67
|
throw new ExecutorError(API_NOT_AUTHORIZED, 'Not authorized');
|
|
73
68
|
}
|
|
74
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @override
|
|
73
|
+
*/
|
|
74
|
+
public async getUser(): Promise<UserSchema | null> {
|
|
75
|
+
const supabase = await this.supabase.getSupabase();
|
|
76
|
+
|
|
77
|
+
const { data } = await supabase.auth.getUser();
|
|
78
|
+
|
|
79
|
+
return data.user ? this.supabase.toUserSchema(data.user) : null;
|
|
80
|
+
}
|
|
75
81
|
}
|
|
@@ -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,
|