@qlover/create-app 0.7.13 → 0.7.15
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 +89 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/README.en.md +131 -0
- package/dist/templates/next-app/README.md +115 -20
- package/dist/templates/next-app/config/IOCIdentifier.ts +14 -1
- package/dist/templates/next-app/config/Identifier/index.ts +1 -0
- package/dist/templates/next-app/config/Identifier/page.admin.ts +48 -0
- package/dist/templates/next-app/config/i18n/admin18n.ts +33 -0
- package/dist/templates/next-app/config/i18n/index.ts +3 -1
- package/dist/templates/next-app/docs/en/api.md +387 -0
- package/dist/templates/next-app/docs/en/component.md +544 -0
- package/dist/templates/next-app/docs/en/database.md +496 -0
- package/dist/templates/next-app/docs/en/development-guide.md +727 -0
- package/dist/templates/next-app/docs/en/env.md +563 -0
- package/dist/templates/next-app/docs/en/i18n.md +287 -0
- package/dist/templates/next-app/docs/en/index.md +166 -0
- package/dist/templates/next-app/docs/en/page.md +457 -0
- package/dist/templates/next-app/docs/en/project-structure.md +177 -0
- package/dist/templates/next-app/docs/en/router.md +427 -0
- package/dist/templates/next-app/docs/en/theme.md +532 -0
- package/dist/templates/next-app/docs/en/validator.md +478 -0
- package/dist/templates/next-app/docs/zh/api.md +387 -0
- package/dist/templates/next-app/docs/zh/component.md +544 -0
- package/dist/templates/next-app/docs/zh/database.md +496 -0
- package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
- package/dist/templates/next-app/docs/zh/env.md +563 -0
- package/dist/templates/next-app/docs/zh/i18n.md +287 -0
- package/dist/templates/next-app/docs/zh/index.md +166 -0
- package/dist/templates/next-app/docs/zh/page.md +457 -0
- package/dist/templates/next-app/docs/zh/project-structure.md +177 -0
- package/dist/templates/next-app/docs/zh/router.md +427 -0
- package/dist/templates/next-app/docs/zh/theme.md +532 -0
- package/dist/templates/next-app/docs/zh/validator.md +476 -0
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +2 -2
- package/dist/templates/next-app/next.config.ts +1 -1
- package/dist/templates/next-app/package.json +3 -1
- package/dist/templates/next-app/public/locales/en.json +8 -1
- package/dist/templates/next-app/public/locales/zh.json +8 -1
- package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +14 -16
- package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +10 -3
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/page.tsx +2 -3
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
- package/dist/templates/next-app/src/app/api/ai/completions/route.ts +32 -0
- package/dist/templates/next-app/src/base/cases/AppConfig.ts +3 -0
- package/dist/templates/next-app/src/base/cases/ChatAction.ts +21 -0
- package/dist/templates/next-app/src/base/cases/FocusBarAction.ts +36 -0
- package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +1 -3
- package/dist/templates/next-app/src/base/services/AdminUserService.ts +1 -1
- package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +1 -1
- package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +23 -1
- package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +2 -2
- package/dist/templates/next-app/src/base/types/PageProps.ts +1 -1
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +1 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +1 -0
- package/dist/templates/next-app/src/core/globals.ts +2 -0
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +4 -1
- package/dist/templates/next-app/src/{base/cases → server}/PageParams.ts +1 -1
- package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +31 -0
- package/dist/templates/next-app/src/server/port/DBTableInterface.ts +1 -1
- package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +6 -4
- package/dist/templates/next-app/src/server/services/AIService.ts +43 -0
- package/dist/templates/next-app/src/server/services/ApiUserService.ts +1 -1
- package/dist/templates/next-app/src/server/{SupabaseBridge.ts → sqlBridges/SupabaseBridge.ts} +16 -11
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +4 -4
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +1 -1
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +32 -25
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +12 -26
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +37 -5
- package/dist/templates/next-app/src/uikit/components/ChatRoot.tsx +17 -0
- package/dist/templates/next-app/src/uikit/components/ClientSeo.tsx +36 -0
- package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +5 -6
- package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +2 -0
- package/dist/templates/next-app/src/uikit/components/With.tsx +17 -0
- package/dist/templates/next-app/src/uikit/components/chat/ChatActionInterface.ts +30 -0
- package/dist/templates/next-app/src/uikit/components/chat/ChatFocusBar.tsx +65 -0
- package/dist/templates/next-app/src/uikit/components/chat/ChatMessages.tsx +59 -0
- package/dist/templates/next-app/src/uikit/components/chat/ChatWrap.tsx +28 -0
- package/dist/templates/next-app/src/uikit/components/chat/FocusBarActionInterface.ts +19 -0
- package/package.json +1 -1
- package/dist/templates/next-app/docs/env.md +0 -94
- package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +0 -21
- package/dist/templates/next-app/src/base/port/DBMigrationInterface.ts +0 -92
- package/dist/templates/next-app/src/base/port/MigrationApiInterface.ts +0 -3
- package/dist/templates/next-app/src/base/port/ServerApiResponseInterface.ts +0 -6
- package/dist/templates/next-app/src/base/services/migrations/MigrationsApi.ts +0 -43
- package/dist/templates/next-app/config/i18n/{HomeI18n .ts → HomeI18n.ts} +0 -0
- package/dist/templates/next-app/{build → make}/generateLocales.ts +2 -2
- /package/dist/templates/next-app/src/{base → server}/port/PaginationInterface.ts +0 -0
- /package/dist/templates/next-app/src/{base → server}/port/ParamsHandlerInterface.ts +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ExecutorError } from '@qlover/fe-corekit';
|
|
2
|
+
import { NextResponse } from 'next/server';
|
|
3
|
+
import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
|
|
4
|
+
import { AppErrorApi } from '@/server/AppErrorApi';
|
|
5
|
+
import { AppSuccessApi } from '@/server/AppSuccessApi';
|
|
6
|
+
import { AIService } from '@/server/services/AIService';
|
|
7
|
+
|
|
8
|
+
export async function GET() {
|
|
9
|
+
const server = new BootstrapServer();
|
|
10
|
+
|
|
11
|
+
const result = await server.execNoError(async ({ parameters: { IOC } }) => {
|
|
12
|
+
// const requestBody = await req.json();
|
|
13
|
+
|
|
14
|
+
const result = await IOC(AIService).completions([
|
|
15
|
+
{
|
|
16
|
+
role: 'user',
|
|
17
|
+
content: 'hello'
|
|
18
|
+
}
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
return result;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (result instanceof ExecutorError) {
|
|
25
|
+
console.log(result);
|
|
26
|
+
return NextResponse.json(new AppErrorApi(result.id, result.message), {
|
|
27
|
+
status: 400
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return NextResponse.json(new AppSuccessApi(result));
|
|
32
|
+
}
|
|
@@ -31,4 +31,7 @@ export class AppConfig implements EnvConfigInterface {
|
|
|
31
31
|
* @example '1 year'
|
|
32
32
|
*/
|
|
33
33
|
readonly jwtExpiresIn: StringValue = '30 days';
|
|
34
|
+
|
|
35
|
+
readonly openaiBaseUrl: string = process.env.CEREBRAS_BASE_URL!;
|
|
36
|
+
readonly openaiApiKey: string = process.env.CEREBRAS_API_KEY!;
|
|
34
37
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { injectable } from 'inversify';
|
|
2
|
+
import {
|
|
3
|
+
ChatActionInterface,
|
|
4
|
+
type MessageInterface,
|
|
5
|
+
type ChatStateInterface
|
|
6
|
+
} from '@/uikit/components/chat/ChatActionInterface';
|
|
7
|
+
|
|
8
|
+
class ChatState implements ChatStateInterface {
|
|
9
|
+
messages: MessageInterface[] = [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@injectable()
|
|
13
|
+
export class ChatAction extends ChatActionInterface<ChatStateInterface> {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(() => new ChatState());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
focus(): void {
|
|
19
|
+
console.log('focus');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { injectable } from 'inversify';
|
|
2
|
+
|
|
3
|
+
import type { FocusBarStateInterface } from '@/uikit/components/chat/FocusBarActionInterface';
|
|
4
|
+
import { FocusBarActionInterface } from '@/uikit/components/chat/FocusBarActionInterface';
|
|
5
|
+
import { RequestState } from './RequestState';
|
|
6
|
+
|
|
7
|
+
class FocusBarState implements FocusBarStateInterface {
|
|
8
|
+
showHistoryArea = false;
|
|
9
|
+
inputValue = '';
|
|
10
|
+
sendState = new RequestState();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@injectable()
|
|
14
|
+
export class FocusBarAction extends FocusBarActionInterface<FocusBarStateInterface> {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(() => new FocusBarState());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
sendMessage(_message: string): Promise<void> {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
this.emit(this.cloneState({ sendState: new RequestState(true) }));
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
this.emit(this.cloneState({ sendState: new RequestState().end() }));
|
|
24
|
+
resolve();
|
|
25
|
+
}, 1000);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setInputValue(value: string): void {
|
|
30
|
+
this.emit(this.cloneState({ inputValue: value }));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
clearInput(): void {
|
|
34
|
+
this.emit(this.cloneState({ inputValue: '' }));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StoreInterface } from '@qlover/corekit-bridge';
|
|
2
2
|
import { RequestState } from '../cases/RequestState';
|
|
3
|
-
import type { PaginationInterface } from '
|
|
3
|
+
import type { PaginationInterface } from '../../server/port/PaginationInterface';
|
|
4
4
|
import type { StoreStateInterface } from '@qlover/corekit-bridge';
|
|
5
5
|
|
|
6
6
|
export interface AdminPageListParams {
|
|
@@ -34,8 +34,6 @@ export abstract class AdminPageInterface<
|
|
|
34
34
|
try {
|
|
35
35
|
const result = await this.fetchList(this.state.listParams);
|
|
36
36
|
|
|
37
|
-
console.log('jj result', result);
|
|
38
|
-
|
|
39
37
|
this.emit(
|
|
40
38
|
this.cloneState({
|
|
41
39
|
initState: new RequestState(false, result).end()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
|
+
import type { PaginationInterface } from '@/server/port/PaginationInterface';
|
|
2
3
|
import {
|
|
3
4
|
AdminPageInterface,
|
|
4
5
|
type AdminPageListParams,
|
|
@@ -6,7 +7,6 @@ import {
|
|
|
6
7
|
} from '../port/AdminPageInterface';
|
|
7
8
|
import { AdminUserApi } from './adminApi/AdminUserApi';
|
|
8
9
|
import { RequestState } from '../cases/RequestState';
|
|
9
|
-
import type { PaginationInterface } from '../port/PaginationInterface';
|
|
10
10
|
|
|
11
11
|
@injectable()
|
|
12
12
|
export class AdminUserService extends AdminPageInterface<AdminPageState> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
2
|
import type { AdminPageListParams } from '@/base/port/AdminPageInterface';
|
|
3
|
-
import type { PaginationInterface } from '@/
|
|
3
|
+
import type { PaginationInterface } from '@/server/port/PaginationInterface';
|
|
4
4
|
import {
|
|
5
5
|
AppApiRequester,
|
|
6
6
|
type AppApiConfig,
|
|
@@ -6,10 +6,13 @@ import {
|
|
|
6
6
|
} from '@qlover/fe-corekit';
|
|
7
7
|
import type { AppApiErrorInterface } from '@/base/port/AppApiInterface';
|
|
8
8
|
import type { AppApiConfig } from './AppApiRequester';
|
|
9
|
+
import type { LoggerInterface } from '@qlover/logger';
|
|
9
10
|
|
|
10
11
|
export class AppApiPlugin implements ExecutorPlugin {
|
|
11
12
|
readonly pluginName = 'AppApiPlugin';
|
|
12
13
|
|
|
14
|
+
constructor(protected logger: LoggerInterface) {}
|
|
15
|
+
|
|
13
16
|
isAppApiErrorInterface(value: unknown): value is AppApiErrorInterface {
|
|
14
17
|
return (
|
|
15
18
|
typeof value === 'object' &&
|
|
@@ -21,8 +24,16 @@ export class AppApiPlugin implements ExecutorPlugin {
|
|
|
21
24
|
);
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
onSuccess(context: ExecutorContext<
|
|
27
|
+
onSuccess(context: ExecutorContext<AppApiConfig>): void | Promise<void> {
|
|
25
28
|
const response = context.returnValue;
|
|
29
|
+
const { parameters } = context;
|
|
30
|
+
|
|
31
|
+
this.logger.info(
|
|
32
|
+
`%c[AppApi ${parameters.method} ${parameters.url}]%c - ${new Date().toLocaleString()}`,
|
|
33
|
+
'color: #0f0;',
|
|
34
|
+
'color: inherit;',
|
|
35
|
+
response
|
|
36
|
+
);
|
|
26
37
|
|
|
27
38
|
if (this.isAppApiErrorInterface(response)) {
|
|
28
39
|
throw new Error(response.message || response.id);
|
|
@@ -34,6 +45,8 @@ export class AppApiPlugin implements ExecutorPlugin {
|
|
|
34
45
|
): Promise<ExecutorError | void> {
|
|
35
46
|
const { error, parameters } = context;
|
|
36
47
|
|
|
48
|
+
this.loggerError(parameters, error);
|
|
49
|
+
|
|
37
50
|
if (error instanceof RequestError && parameters.responseType === 'json') {
|
|
38
51
|
// @ts-expect-error response is not defined in Error
|
|
39
52
|
let response = error?.response;
|
|
@@ -60,4 +73,13 @@ export class AppApiPlugin implements ExecutorPlugin {
|
|
|
60
73
|
return {};
|
|
61
74
|
}
|
|
62
75
|
}
|
|
76
|
+
|
|
77
|
+
protected loggerError(config: AppApiConfig, error: unknown): void {
|
|
78
|
+
this.logger.error(
|
|
79
|
+
`%c[AppApi ${config.method} ${config.url}]%c - ${new Date().toLocaleString()}`,
|
|
80
|
+
'color: #f00;',
|
|
81
|
+
'color: inherit;',
|
|
82
|
+
error
|
|
83
|
+
);
|
|
84
|
+
}
|
|
63
85
|
}
|
|
@@ -3,6 +3,7 @@ import { FetchURLPlugin } from '@qlover/fe-corekit';
|
|
|
3
3
|
import { DialogErrorPlugin } from '@/base/cases/DialogErrorPlugin';
|
|
4
4
|
import { RequestEncryptPlugin } from '@/base/cases/RequestEncryptPlugin';
|
|
5
5
|
import { StringEncryptor } from '@/base/cases/StringEncryptor';
|
|
6
|
+
import { I } from '@config/IOCIdentifier';
|
|
6
7
|
import { AppApiPlugin } from './AppApiPlugin';
|
|
7
8
|
import { AppApiRequester } from './AppApiRequester';
|
|
8
9
|
import type { AppApiConfig } from './AppApiRequester';
|
|
@@ -27,9 +28,8 @@ export class AppUserApiBootstrap implements BootstrapExecutorPlugin {
|
|
|
27
28
|
requestDataSerializer: this.requestDataSerializer.bind(this)
|
|
28
29
|
})
|
|
29
30
|
);
|
|
30
|
-
appUserApi.usePlugin(new AppApiPlugin());
|
|
31
|
+
appUserApi.usePlugin(new AppApiPlugin(ioc.get(I.Logger)));
|
|
31
32
|
appUserApi.usePlugin(ioc.get(DialogErrorPlugin));
|
|
32
|
-
console.log('jj AppUserApiBootstrap success');
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
protected requestDataSerializer(
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type IOCRegisterInterface
|
|
8
8
|
} from '@qlover/corekit-bridge';
|
|
9
9
|
import type { IocRegisterOptions } from '@/base/port/IOCInterface';
|
|
10
|
+
import { SupabaseBridge } from '@/server/sqlBridges/SupabaseBridge';
|
|
10
11
|
import { IOCIdentifier as I } from '@config/IOCIdentifier';
|
|
11
12
|
|
|
12
13
|
export class ServerIOCRegister
|
|
@@ -45,7 +46,9 @@ export class ServerIOCRegister
|
|
|
45
46
|
);
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
protected registerImplement(
|
|
49
|
+
protected registerImplement(ioc: IOCContainerInterface): void {
|
|
50
|
+
ioc.bind(I.DBBridgeInterface, ioc.get(SupabaseBridge));
|
|
51
|
+
}
|
|
49
52
|
|
|
50
53
|
protected registerCommon(_ioc: IOCContainerInterface): void {}
|
|
51
54
|
|
|
@@ -2,7 +2,7 @@ import { notFound } from 'next/navigation';
|
|
|
2
2
|
import { getMessages, getTranslations } from 'next-intl/server';
|
|
3
3
|
import { i18nConfig } from '@config/i18n';
|
|
4
4
|
import type { LocaleType, PageI18nInterface } from '@config/i18n';
|
|
5
|
-
import type { ParamsHandlerInterface as ParamsHandlerInterface } from '
|
|
5
|
+
import type { ParamsHandlerInterface as ParamsHandlerInterface } from './port/ParamsHandlerInterface';
|
|
6
6
|
|
|
7
7
|
export interface PageWithParams {
|
|
8
8
|
params?: Promise<PageParamsType>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type WhereOperation = '=' | '!=' | '>' | '<' | '>=' | '<=';
|
|
2
|
+
export type Where = [string, WhereOperation, string | number];
|
|
3
|
+
|
|
4
|
+
export interface BridgeEvent {
|
|
5
|
+
table: string;
|
|
6
|
+
fields?: string | string[];
|
|
7
|
+
where?: Where[];
|
|
8
|
+
data?: unknown;
|
|
9
|
+
page?: number;
|
|
10
|
+
pageSize?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PaginationInfo {
|
|
14
|
+
total: number;
|
|
15
|
+
page: number;
|
|
16
|
+
pageSize: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface DBBridgeResponse<T> {
|
|
20
|
+
error?: unknown;
|
|
21
|
+
data: T;
|
|
22
|
+
pagination?: PaginationInfo;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DBBridgeInterface {
|
|
26
|
+
add(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
27
|
+
update(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
28
|
+
delete(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
29
|
+
get(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
|
|
30
|
+
pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>>;
|
|
31
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
2
|
import { isEmpty, last } from 'lodash';
|
|
3
|
-
import type { DBBridgeInterface } from '@/
|
|
4
|
-
import type { PaginationInterface } from '@/
|
|
3
|
+
import type { DBBridgeInterface } from '@/server/port/DBBridgeInterface';
|
|
4
|
+
import type { PaginationInterface } from '@/server/port/PaginationInterface';
|
|
5
5
|
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
6
|
-
import {
|
|
6
|
+
import { I } from '@config/IOCIdentifier';
|
|
7
7
|
import type { UserRepositoryInterface } from '../port/UserRepositoryInterface';
|
|
8
8
|
|
|
9
9
|
@injectable()
|
|
@@ -21,7 +21,9 @@ export class UserRepository implements UserRepositoryInterface {
|
|
|
21
21
|
'updated_at'
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
-
constructor(
|
|
24
|
+
constructor(
|
|
25
|
+
@inject(I.DBBridgeInterface) protected dbBridge: DBBridgeInterface
|
|
26
|
+
) {}
|
|
25
27
|
|
|
26
28
|
getAll(): Promise<unknown> {
|
|
27
29
|
return this.dbBridge.get({ table: this.name });
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { inject, injectable } from 'inversify';
|
|
2
|
+
import OpenAI from 'openai';
|
|
3
|
+
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
4
|
+
import { I } from '@config/IOCIdentifier';
|
|
5
|
+
import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions';
|
|
6
|
+
|
|
7
|
+
@injectable()
|
|
8
|
+
export class AIService {
|
|
9
|
+
protected apiKey: string;
|
|
10
|
+
protected baseUrl: string;
|
|
11
|
+
protected client: OpenAI;
|
|
12
|
+
|
|
13
|
+
constructor(@inject(I.AppConfig) appConfig: AppConfig) {
|
|
14
|
+
this.apiKey = appConfig.openaiApiKey;
|
|
15
|
+
this.baseUrl = appConfig.openaiBaseUrl;
|
|
16
|
+
|
|
17
|
+
console.log(this.apiKey, this.baseUrl);
|
|
18
|
+
this.client = new OpenAI({
|
|
19
|
+
apiKey: this.apiKey,
|
|
20
|
+
baseURL: this.baseUrl
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async completions(messages: ChatCompletionMessageParam[]): Promise<unknown> {
|
|
25
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
26
|
+
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
messages: messages,
|
|
31
|
+
model: 'claude-sonnet-4-20250514'
|
|
32
|
+
}),
|
|
33
|
+
headers: {
|
|
34
|
+
Authorization: `token ${this.apiKey}`,
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
Accept: 'application/json'
|
|
37
|
+
},
|
|
38
|
+
mode: 'cors'
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return await response.json();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
|
-
import type { PaginationInterface } from '@/
|
|
2
|
+
import type { PaginationInterface } from '@/server/port/PaginationInterface';
|
|
3
3
|
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
4
4
|
import { UserRepository } from '../repositorys/UserRepository';
|
|
5
5
|
import { PaginationValidator } from '../validators/PaginationValidator';
|
package/dist/templates/next-app/src/server/{SupabaseBridge.ts → sqlBridges/SupabaseBridge.ts}
RENAMED
|
@@ -9,8 +9,9 @@ import type { AppConfig } from '@/base/cases/AppConfig';
|
|
|
9
9
|
import type {
|
|
10
10
|
BridgeEvent,
|
|
11
11
|
DBBridgeInterface,
|
|
12
|
+
DBBridgeResponse,
|
|
12
13
|
Where
|
|
13
|
-
} from '@/
|
|
14
|
+
} from '@/server/port/DBBridgeInterface';
|
|
14
15
|
import { I } from '@config/IOCIdentifier';
|
|
15
16
|
import type { LoggerInterface } from '@qlover/logger';
|
|
16
17
|
import type { PostgrestFilterBuilder } from '@supabase/postgrest-js';
|
|
@@ -24,6 +25,9 @@ const whereHandlerMaps = {
|
|
|
24
25
|
'<=': 'lte'
|
|
25
26
|
};
|
|
26
27
|
|
|
28
|
+
export type SupabaseBridgeResponse<T> = DBBridgeResponse<T> &
|
|
29
|
+
PostgrestResponse<T>;
|
|
30
|
+
|
|
27
31
|
@injectable()
|
|
28
32
|
export class SupabaseBridge implements DBBridgeInterface {
|
|
29
33
|
protected supabase: SupabaseClient;
|
|
@@ -42,19 +46,20 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
42
46
|
return this.supabase;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
async execSql(sql: string): Promise<
|
|
49
|
+
async execSql(sql: string): Promise<SupabaseBridgeResponse<unknown>> {
|
|
46
50
|
const res = await this.supabase.rpc('exec_sql', { sql });
|
|
47
51
|
return this.catch(res);
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
protected async catch(
|
|
51
55
|
result: PostgrestSingleResponse<unknown>
|
|
52
|
-
): Promise<
|
|
56
|
+
): Promise<SupabaseBridgeResponse<unknown>> {
|
|
53
57
|
if (result.error) {
|
|
54
58
|
this.logger.info(result);
|
|
55
59
|
throw new Error(result.error.message);
|
|
56
60
|
}
|
|
57
|
-
|
|
61
|
+
|
|
62
|
+
return result as SupabaseBridgeResponse<unknown>;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
protected handleWhere(
|
|
@@ -79,7 +84,7 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
async add(event: BridgeEvent): Promise<
|
|
87
|
+
async add(event: BridgeEvent): Promise<DBBridgeResponse<unknown>> {
|
|
83
88
|
const { table, data } = event;
|
|
84
89
|
if (!data) {
|
|
85
90
|
throw new Error('Data is required for add operation');
|
|
@@ -91,7 +96,7 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
91
96
|
return this.catch(res);
|
|
92
97
|
}
|
|
93
98
|
|
|
94
|
-
async update(event: BridgeEvent): Promise<
|
|
99
|
+
async update(event: BridgeEvent): Promise<DBBridgeResponse<unknown>> {
|
|
95
100
|
const { table, data, where } = event;
|
|
96
101
|
if (!data) {
|
|
97
102
|
throw new Error('Data is required for update operation');
|
|
@@ -104,7 +109,7 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
104
109
|
return this.catch(await handler);
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
async delete(event: BridgeEvent): Promise<
|
|
112
|
+
async delete(event: BridgeEvent): Promise<DBBridgeResponse<unknown>> {
|
|
108
113
|
const { table, where } = event;
|
|
109
114
|
const handler = this.supabase.from(table).delete();
|
|
110
115
|
|
|
@@ -113,7 +118,7 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
113
118
|
return this.catch(await handler);
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
async get(event: BridgeEvent): Promise<
|
|
121
|
+
async get(event: BridgeEvent): Promise<SupabaseBridgeResponse<unknown>> {
|
|
117
122
|
const { table, fields = '*', where } = event;
|
|
118
123
|
const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
|
|
119
124
|
const handler = this.supabase.from(table).select(selectFields);
|
|
@@ -123,7 +128,7 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
123
128
|
return this.catch(await handler);
|
|
124
129
|
}
|
|
125
130
|
|
|
126
|
-
async pagination(event: BridgeEvent): Promise<
|
|
131
|
+
async pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>> {
|
|
127
132
|
const { table, fields = '*', where, page = 1, pageSize = 10 } = event;
|
|
128
133
|
const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
|
|
129
134
|
|
|
@@ -145,7 +150,7 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
145
150
|
const result = await this.catch(await handler);
|
|
146
151
|
|
|
147
152
|
if (result.error) {
|
|
148
|
-
return result as
|
|
153
|
+
return result as DBBridgeResponse<unknown[]>;
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
return {
|
|
@@ -154,6 +159,6 @@ export class SupabaseBridge implements DBBridgeInterface {
|
|
|
154
159
|
count: countResult.count,
|
|
155
160
|
status: result.status,
|
|
156
161
|
statusText: result.statusText
|
|
157
|
-
}
|
|
162
|
+
} as DBBridgeResponse<unknown[]>;
|
|
158
163
|
}
|
|
159
164
|
}
|
|
@@ -17,13 +17,13 @@ export interface LoginValidatorData {
|
|
|
17
17
|
password: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const emailSchema = z.email({
|
|
20
|
+
const emailSchema = z.string().email({ message: V_EMAIL_INVALID });
|
|
21
21
|
|
|
22
22
|
const passwordSchema = z
|
|
23
23
|
.string()
|
|
24
|
-
.min(6, {
|
|
25
|
-
.max(50, {
|
|
26
|
-
.regex(/^\S+$/, {
|
|
24
|
+
.min(6, { message: V_PASSWORD_MIN_LENGTH })
|
|
25
|
+
.max(50, { message: V_PASSWORD_MAX_LENGTH })
|
|
26
|
+
.regex(/^\S+$/, { message: V_PASSWORD_SPECIAL_CHARS });
|
|
27
27
|
|
|
28
28
|
interface ExtendedExecutorError extends ExecutorError {
|
|
29
29
|
issues?: ValidationFaildResult[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type { PaginationInterface } from '@/
|
|
2
|
+
import type { PaginationInterface } from '@/server/port/PaginationInterface';
|
|
3
3
|
import { API_PAGE_INVALID } from '@config/Identifier';
|
|
4
4
|
import {
|
|
5
5
|
type ValidationFaildResult,
|
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
import {
|
|
4
4
|
DashboardOutlined,
|
|
5
5
|
UserOutlined,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
VerticalAlignBottomOutlined,
|
|
7
|
+
VerticalAlignTopOutlined
|
|
8
8
|
} from '@ant-design/icons';
|
|
9
9
|
import { Layout, Menu } from 'antd';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
|
-
import React, {
|
|
11
|
+
import React, { useMemo, type HTMLAttributes } from 'react';
|
|
12
12
|
import { AdminPageManager } from '@/base/cases/AdminPageManager';
|
|
13
13
|
import { BaseHeader } from './BaseHeader';
|
|
14
|
+
import { LanguageSwitcher } from './LanguageSwitcher';
|
|
14
15
|
import { LocaleLink } from './LocaleLink';
|
|
16
|
+
import { LogoutButton } from './LogoutButton';
|
|
17
|
+
import { ThemeSwitcher } from './ThemeSwitcher';
|
|
15
18
|
import { useIOC } from '../hook/useIOC';
|
|
16
19
|
import { useStore } from '../hook/useStore';
|
|
17
|
-
import type { RenderLeftFunction } from './BaseHeader';
|
|
18
20
|
import type { ItemType } from 'antd/es/menu/interface';
|
|
19
21
|
|
|
20
22
|
const { Sider } = Layout;
|
|
@@ -33,7 +35,6 @@ export function AdminLayout(props: AdminLayoutProps) {
|
|
|
33
35
|
const { children, className, mainClassName, ...rest } = props;
|
|
34
36
|
|
|
35
37
|
const page = useIOC(AdminPageManager);
|
|
36
|
-
|
|
37
38
|
const collapsedSidebar = useStore(page, page.selectors.collapsedSidebar);
|
|
38
39
|
const navItems = useStore(page, page.selectors.navItems);
|
|
39
40
|
|
|
@@ -60,40 +61,46 @@ export function AdminLayout(props: AdminLayoutProps) {
|
|
|
60
61
|
});
|
|
61
62
|
}, [navItems]);
|
|
62
63
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
{collapsedSidebar ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
71
|
-
</span>
|
|
72
|
-
|
|
73
|
-
{defaultElement}
|
|
74
|
-
</div>
|
|
75
|
-
),
|
|
76
|
-
[collapsedSidebar, page]
|
|
77
|
-
);
|
|
64
|
+
const rightActions = useMemo(() => {
|
|
65
|
+
return [
|
|
66
|
+
<LanguageSwitcher key="language-switcher" />,
|
|
67
|
+
<ThemeSwitcher key="theme-switcher" />,
|
|
68
|
+
<LogoutButton key="logout-button" />
|
|
69
|
+
];
|
|
70
|
+
}, []);
|
|
78
71
|
|
|
79
72
|
return (
|
|
80
73
|
<Layout data-testid="AdminLayout" className={className} {...rest}>
|
|
81
|
-
<div className="overflow-auto h-screen sticky top-0 bottom-0 scrollbar-thin scrollbar-gutter-stable">
|
|
74
|
+
<div className="overflow-y-auto overflow-x-hidden h-screen sticky top-0 bottom-0 scrollbar-thin scrollbar-gutter-stable">
|
|
82
75
|
<Sider
|
|
83
|
-
className="h-full"
|
|
76
|
+
className="h-full relative"
|
|
84
77
|
onCollapse={() => page.toggleSidebar()}
|
|
85
78
|
collapsed={collapsedSidebar}
|
|
79
|
+
collapsedWidth={46}
|
|
86
80
|
>
|
|
87
|
-
<div className="demo-logo-vertical" />
|
|
88
81
|
<Menu mode="inline" items={sidebarItems} />
|
|
82
|
+
|
|
83
|
+
<div
|
|
84
|
+
data-testid="ToggleSidebarButton"
|
|
85
|
+
className="absolute w-2 right-0 top-0 bottom-0 bg-secondary cursor-pointer hover:bg-elevated flex items-center justify-center"
|
|
86
|
+
onClick={() => page.toggleSidebar()}
|
|
87
|
+
>
|
|
88
|
+
<span className="text-text scale-75 rotate-x-90">
|
|
89
|
+
{collapsedSidebar ? (
|
|
90
|
+
<VerticalAlignTopOutlined />
|
|
91
|
+
) : (
|
|
92
|
+
<VerticalAlignBottomOutlined />
|
|
93
|
+
)}
|
|
94
|
+
</span>
|
|
95
|
+
</div>
|
|
89
96
|
</Sider>
|
|
90
97
|
</div>
|
|
91
98
|
|
|
92
99
|
<Layout>
|
|
93
100
|
<BaseHeader
|
|
94
101
|
href="/admin"
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
className="max-w-full pl-0"
|
|
103
|
+
rightActions={rightActions}
|
|
97
104
|
/>
|
|
98
105
|
<main
|
|
99
106
|
className={clsx('p-2 bg-primary text-text flex-1', mainClassName)}
|