add config module
This commit is contained in:
parent
88ba3f5a16
commit
e5912600ce
@ -1,41 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
|
||||
|
||||
import { AppInterceptor } from '@/modules/core/providers/app.interceptor';
|
||||
|
||||
import { MEILI_CONFIG } from '@/modules/meilisearch/meili.config';
|
||||
import { MeiliModule } from '@/modules/meilisearch/meili.module';
|
||||
|
||||
import { content, database } from './config';
|
||||
|
||||
import { DEFAULT_VALIDATION_CONFIG } from './modules/content/constants';
|
||||
import { ContentModule } from './modules/content/content.module';
|
||||
import { CoreModule } from './modules/core/core.module';
|
||||
import { AppFilter } from './modules/core/providers/app.filter';
|
||||
import { AppPipe } from './modules/core/providers/app.pipe';
|
||||
import { DatabaseModule } from './modules/database/database.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ContentModule.forRoot(content),
|
||||
CoreModule.forRoot(),
|
||||
DatabaseModule.forRoot(database),
|
||||
MeiliModule.forRoot(MEILI_CONFIG),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_PIPE,
|
||||
useValue: new AppPipe(DEFAULT_VALIDATION_CONFIG),
|
||||
},
|
||||
{
|
||||
provide: APP_INTERCEPTOR,
|
||||
useClass: AppInterceptor,
|
||||
},
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: AppFilter,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
9
src/config/app.config.ts
Normal file
9
src/config/app.config.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { toNumber } from 'lodash';
|
||||
|
||||
import { createAppConfig } from '@/modules/core/config';
|
||||
|
||||
export const app = createAppConfig((configure) => ({
|
||||
port: configure.env.get<number>('APP_PORT', (v) => toNumber(v), 3099),
|
||||
prefix: 'api',
|
||||
});
|
||||
});
|
@ -1,5 +1,6 @@
|
||||
import { ContentConfig } from '@/modules/content/types';
|
||||
import { createContentConfig } from '@/modules/content/config';
|
||||
|
||||
export const content = (): ContentConfig => ({
|
||||
SearchType: 'meili',
|
||||
});
|
||||
export const content = createContentConfig(() => ({
|
||||
searchType: 'meili',
|
||||
htmlEnabled: false,
|
||||
}));
|
||||
|
@ -1,15 +1,17 @@
|
||||
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
import { toNumber } from 'lodash';
|
||||
|
||||
export const database = (): TypeOrmModuleOptions => ({
|
||||
charset: 'utf8mb4',
|
||||
logging: ['error'],
|
||||
type: 'mysql',
|
||||
host: '192.168.50.26',
|
||||
port: 3306,
|
||||
username: '3r',
|
||||
password: '12345678',
|
||||
database: '3r',
|
||||
synchronize: true,
|
||||
autoLoadEntities: true,
|
||||
timezone: '+08:00',
|
||||
});
|
||||
import { createDBConfig } from '@/modules/database/config';
|
||||
|
||||
export const database = createDBConfig((configure) => ({
|
||||
common: { synchronize: true },
|
||||
connections: [
|
||||
{
|
||||
type: 'mysql',
|
||||
host: configure.env.get('DB_HOST', '127.0.0.1'),
|
||||
port: configure.env.get<number>('DB_PORT', (v) => toNumber(v), 3306),
|
||||
username: configure.env.get('DB_USERNAME', 'root'),
|
||||
password: configure.env.get('DB_PASSWORD', '12345678'),
|
||||
database: configure.env.get('DB_NAME', '3r'),
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
@ -1,2 +1,4 @@
|
||||
export * from './database.config';
|
||||
export * from './content.config';
|
||||
export * from './app.config';
|
||||
export * from './meili.config';
|
||||
|
8
src/config/meili.config.ts
Normal file
8
src/config/meili.config.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { createMeiliConfig } from '../modules/meilisearch/config';
|
||||
|
||||
export const MEILI_CONFIG = createMeiliConfig((configure) => [
|
||||
{
|
||||
name: 'default',
|
||||
host: 'http://192.168.50.26:7700',
|
||||
},
|
||||
]);
|
23
src/main.ts
23
src/main.ts
@ -1,21 +1,4 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { createApp, listened, startApp } from './modules/core/helpers/app';
|
||||
import { createOptions } from './options';
|
||||
|
||||
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
|
||||
import { useContainer } from 'class-validator';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter(), {
|
||||
cors: true,
|
||||
logger: ['error', 'warn'],
|
||||
});
|
||||
app.setGlobalPrefix('api');
|
||||
useContainer(app.select(AppModule), { fallbackOnErrors: true });
|
||||
await app.listen(process.env.PORT ?? 3000, () => {
|
||||
console.log('api: http://localhost:3000');
|
||||
});
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
startApp(createApp(createOptions), listened);
|
||||
|
@ -46,6 +46,16 @@ export class Env {
|
||||
return this.run() === EnvironmentType.DEVELOPMENT || this.run() === EnvironmentType.DEV;
|
||||
}
|
||||
|
||||
get(): { [key: string]: string };
|
||||
|
||||
get<T extends BaseType = string>(key: string): T;
|
||||
|
||||
get<T extends BaseType = string>(key: string, parseTo?: ParseType<T>): T;
|
||||
|
||||
get<T extends BaseType = string>(key: string, defaultValue?: T): T;
|
||||
|
||||
get<T extends BaseType = string>(key: string, parseTo?: ParseType<T>, defaultValue?: T): T;
|
||||
|
||||
get<T extends BaseType = string>(key?: string, parseTo?: ParseType<T> | T, defaultValue?: T) {
|
||||
if (!key) {
|
||||
return process.env;
|
||||
|
27
src/modules/config/utils.ts
Normal file
27
src/modules/config/utils.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { isNil } from 'lodash';
|
||||
|
||||
import { ConnectionOption, ConnectionRst } from './types';
|
||||
|
||||
export const createConnectionOptions = <T extends RecordAny>(
|
||||
config: ConnectionOption<T> | ConnectionOption<T>[],
|
||||
) => {
|
||||
const options = (
|
||||
Array.isArray(config) ? config : [{ ...config, name: 'default' }]
|
||||
) as ConnectionRst<T>;
|
||||
|
||||
if (options.length <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const names = options.map(({ name }) => name);
|
||||
if (!names.includes('default')) {
|
||||
options[0].name = 'default';
|
||||
}
|
||||
|
||||
return options
|
||||
.filter(({ name }) => !isNil(name))
|
||||
.reduce((o, n) => {
|
||||
const oldNames = o.map(({ name }) => name) as string[];
|
||||
return oldNames.includes(n.name) ? o : [...o, n];
|
||||
}, []);
|
||||
};
|
12
src/modules/content/config.ts
Normal file
12
src/modules/content/config.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { ConfigureFactory, ConfigureRegister } from '../config/types';
|
||||
|
||||
import { ContentConfig } from './types';
|
||||
|
||||
export const defauleContentConfig: ContentConfig = { searchType: 'mysql', htmlEnabled: false };
|
||||
|
||||
export const createContentConfig: (
|
||||
register: ConfigureRegister<RePartial<ContentConfig>>,
|
||||
) => ConfigureFactory<ContentConfig> = (register) => ({
|
||||
register,
|
||||
defaultRegister: () => defauleContentConfig,
|
||||
});
|
@ -11,19 +11,19 @@ import { SanitizeService } from '@/modules/content/services/SanitizeService';
|
||||
|
||||
import { PostService } from '@/modules/content/services/post.service';
|
||||
import { PostSubscriber } from '@/modules/content/subscribers/post.subscriber';
|
||||
import { ContentConfig } from '@/modules/content/types';
|
||||
import { DatabaseModule } from '@/modules/database/database.module';
|
||||
|
||||
import { Configure } from '../config/configure';
|
||||
|
||||
import { defauleContentConfig } from './config';
|
||||
import { ContentConfig } from './types';
|
||||
|
||||
@Module({})
|
||||
export class ContentModule {
|
||||
static forRoot(configRegister?: () => ContentConfig): DynamicModule {
|
||||
const config: Required<ContentConfig> = {
|
||||
SearchType: 'mysql',
|
||||
...(configRegister ? configRegister() : {}),
|
||||
};
|
||||
static async forRoot(configure: Configure): Promise<DynamicModule> {
|
||||
const config = await configure.get<ContentConfig>('content', defauleContentConfig);
|
||||
const providers: ModuleMetadata['providers'] = [
|
||||
...Object.values(services),
|
||||
SanitizeService,
|
||||
PostSubscriber,
|
||||
{
|
||||
provide: PostService,
|
||||
@ -47,13 +47,23 @@ export class ContentModule {
|
||||
categoryService,
|
||||
tagRepository,
|
||||
searchService,
|
||||
config.SearchType,
|
||||
config.searchType,
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
if (config.SearchType === 'meili') {
|
||||
const exports: ModuleMetadata['exports'] = [
|
||||
...Object.values(services),
|
||||
PostService,
|
||||
DatabaseModule.forRepository(Object.values(repositories)),
|
||||
];
|
||||
if (config.searchType === 'meili') {
|
||||
providers.push(services.SearchService);
|
||||
exports.push(SearchService);
|
||||
}
|
||||
if (config.htmlEnabled) {
|
||||
providers.push(SanitizeService);
|
||||
exports.push(SanitizeService);
|
||||
}
|
||||
return {
|
||||
module: ContentModule,
|
||||
@ -63,11 +73,7 @@ export class ContentModule {
|
||||
],
|
||||
controllers: Object.values(controllers),
|
||||
providers,
|
||||
exports: [
|
||||
...Object.values(services),
|
||||
PostService,
|
||||
DatabaseModule.forRepository(Object.values(repositories)),
|
||||
],
|
||||
exports,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Optional } from '@nestjs/common';
|
||||
import { isNil } from 'lodash';
|
||||
import { DataSource, EventSubscriber, ObjectType } from 'typeorm';
|
||||
|
||||
import { Configure } from '@/modules/config/configure';
|
||||
import { PostBodyType } from '@/modules/content/constants';
|
||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||
import { PostRepository } from '@/modules/content/repositories/post.repository';
|
||||
@ -11,14 +14,19 @@ export class PostSubscriber extends BaseSubscriber<PostEntity> {
|
||||
protected entity: ObjectType<PostEntity> = PostEntity;
|
||||
constructor(
|
||||
protected dataSource: DataSource,
|
||||
protected sanitizeService: SanitizeService,
|
||||
protected postRepository: PostRepository,
|
||||
protected configure: Configure,
|
||||
@Optional() protected sanitizeService: SanitizeService,
|
||||
) {
|
||||
super(dataSource);
|
||||
}
|
||||
|
||||
async afterLoad(entity: PostEntity) {
|
||||
if (entity.type === PostBodyType.HTML) {
|
||||
if (
|
||||
(await this.configure.get('content.htmlEnabled')) &&
|
||||
!isNil(this.sanitizeService) &&
|
||||
entity.type === PostBodyType.HTML
|
||||
) {
|
||||
entity.body = this.sanitizeService.sanitize(entity.body);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import { SelectTrashMode } from '@/modules/database/constants';
|
||||
export type SearchType = 'mysql' | 'meili';
|
||||
|
||||
export interface ContentConfig {
|
||||
SearchType?: SearchType;
|
||||
searchType: SearchType;
|
||||
htmlEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface SearchOption {
|
||||
|
30
src/modules/core/config.ts
Normal file
30
src/modules/core/config.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { isNil, toNumber } from 'lodash';
|
||||
|
||||
import { Configure } from '../config/configure';
|
||||
|
||||
import { ConfigureFactory, ConfigureRegister } from '../config/types';
|
||||
|
||||
import { getRandomString, toBoolean } from './helpers';
|
||||
import { AppConfig } from './types';
|
||||
|
||||
export const getDefaultAppConfig = (configure: Configure) => ({
|
||||
name: configure.env.get('APP_NAME', getRandomString()),
|
||||
host: configure.env.get('APP_HOST', '127.0.0.1'),
|
||||
port: configure.env.get('APP_PORT', (v) => toNumber(v), 3000),
|
||||
https: configure.env.get('APP_SSL', (v) => toBoolean(v), false),
|
||||
locale: configure.env.get('APP_LOCALE', 'zh_CN'),
|
||||
fallbackLocale: configure.env.get('APP_FALLBACK_LOCALE', 'en'),
|
||||
});
|
||||
|
||||
export const createAppConfig: (
|
||||
register: ConfigureRegister<RePartial<AppConfig>>,
|
||||
) => ConfigureFactory<AppConfig> = (register) => ({
|
||||
register,
|
||||
defaultRegister: (configure) => getDefaultAppConfig(configure),
|
||||
hook: (configure: Configure, value) => {
|
||||
if (isNil(value.url)) {
|
||||
value.url = `${value.https ? 'https' : 'http'}//${value.host}:${value.port}`;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
});
|
@ -1,9 +1,11 @@
|
||||
import { BadGatewayException, Global, Module, ModuleMetadata, Type } from '@nestjs/common';
|
||||
|
||||
import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
|
||||
import { NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
import chalk from 'chalk';
|
||||
import { useContainer } from 'class-validator';
|
||||
|
||||
import { omit } from 'lodash';
|
||||
import { isNil, omit } from 'lodash';
|
||||
|
||||
import { ConfigModule } from '@/modules/config/config.module';
|
||||
import { Configure } from '@/modules/config/configure';
|
||||
@ -14,7 +16,7 @@ import { CoreModule } from '../core.module';
|
||||
import { AppFilter } from '../providers/app.filter';
|
||||
import { AppInterceptor } from '../providers/app.interceptor';
|
||||
import { AppPipe } from '../providers/app.pipe';
|
||||
import { App, CreateOptions } from '../types';
|
||||
import { App, AppConfig, CreateOptions } from '../types';
|
||||
|
||||
import { CreateModule } from './utils';
|
||||
|
||||
@ -90,3 +92,32 @@ export async function createBootModule(
|
||||
providers,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function startApp(
|
||||
creater: () => Promise<App>,
|
||||
listened: (app: App, startTime: Date) => () => Promise<void>,
|
||||
) {
|
||||
const startTime = new Date();
|
||||
const { container, configure } = await creater();
|
||||
app.container = container;
|
||||
app.configure = configure;
|
||||
const { port, host } = await configure.get<AppConfig>('app');
|
||||
await container.listen(port, host, listened(app, startTime));
|
||||
}
|
||||
|
||||
export async function echoApi(configure: Configure, container: NestFastifyApplication) {
|
||||
const appUrl = await configure.get<string>('app.url');
|
||||
const urlPrefix = await configure.get<string>('api.prefix', undefined);
|
||||
const apiUrl = isNil(urlPrefix)
|
||||
? appUrl
|
||||
: `${appUrl}${urlPrefix.length > 0 ? `/${urlPrefix}` : urlPrefix}`;
|
||||
console.log(`- RestAPI: ${chalk.green.underline(apiUrl)}`);
|
||||
}
|
||||
|
||||
export const listened: (app: App, startyTime: Date) => () => Promise<void> =
|
||||
({ configure, container }, startTime) =>
|
||||
async () => {
|
||||
console.log();
|
||||
await echoApi(configure, container);
|
||||
console.log('used time: ', chalk.cyan(`${new Date().getTime() - startTime.getTime()}`));
|
||||
};
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { Module, ModuleMetadata, Type } from '@nestjs/common';
|
||||
import chalk from 'chalk';
|
||||
import deepmerge from 'deepmerge';
|
||||
import { isNil } from 'lodash';
|
||||
|
||||
import { PanicOption } from '../types';
|
||||
|
||||
export function toBoolean(value?: string | boolean): boolean {
|
||||
if (isNil(value)) {
|
||||
return false;
|
||||
@ -55,3 +58,27 @@ export function CreateModule(
|
||||
Module(metaSetter())(ModuleClass);
|
||||
return ModuleClass;
|
||||
}
|
||||
|
||||
export const getRandomString = (length = 10) => {
|
||||
let result = '';
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
const totalLength = characters.length;
|
||||
|
||||
for (let index = 0; index < length; index++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * totalLength));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export async function panic(option: PanicOption | string) {
|
||||
console.log();
|
||||
if (typeof option === 'string') {
|
||||
console.log(chalk.red(`\n❌ ${option}`));
|
||||
process.exit(1);
|
||||
}
|
||||
const { error, message, exit = true } = option;
|
||||
isNil(error) ? console.log(chalk.red(`\n❌ ${message}`)) : console.log(chalk.red(error));
|
||||
if (exit) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -35,3 +35,29 @@ export interface CreateOptions {
|
||||
export interface ContainerBuilder {
|
||||
(params: { configure: Configure; BootModule: Type<any> }): Promise<NestFastifyApplication>;
|
||||
}
|
||||
|
||||
export interface AppConfig {
|
||||
name: string;
|
||||
|
||||
host: string;
|
||||
|
||||
port: number;
|
||||
|
||||
https: boolean;
|
||||
|
||||
locale: string;
|
||||
|
||||
fallbackLocale: string;
|
||||
|
||||
url?: string;
|
||||
|
||||
prefix?: string;
|
||||
}
|
||||
|
||||
export interface PanicOption {
|
||||
message: string;
|
||||
|
||||
error?: any;
|
||||
|
||||
exit?: boolean;
|
||||
}
|
||||
|
37
src/modules/database/config.ts
Normal file
37
src/modules/database/config.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { ConfigureFactory, ConfigureRegister } from '../config/types';
|
||||
import { createConnectionOptions } from '../config/utils';
|
||||
import { deepMerge } from '../core/helpers';
|
||||
|
||||
import { DBConfig, DBOptions, TypeormOption } from './types';
|
||||
|
||||
export const createDBConfig: (
|
||||
register: ConfigureRegister<RePartial<DBConfig>>,
|
||||
) => ConfigureFactory<DBConfig, DBOptions> = (register) => ({
|
||||
register,
|
||||
hook: (configure, value) => createDBOptions(value),
|
||||
defaultRegister: () => ({
|
||||
common: { charset: 'utf8mb4', logging: ['error'] },
|
||||
connections: [],
|
||||
}),
|
||||
});
|
||||
|
||||
export const createDBOptions = (options: DBConfig) => {
|
||||
const newOptions: DBOptions = {
|
||||
common: deepMerge(
|
||||
{ charset: 'utf8mb4', logging: ['error'] },
|
||||
options.common ?? {},
|
||||
'replace',
|
||||
),
|
||||
connections: createConnectionOptions(options.connections ?? []),
|
||||
};
|
||||
newOptions.connections = newOptions.connections.map((connection) => {
|
||||
const entities = connection.entities ?? [];
|
||||
const newOption = { ...connection, entities };
|
||||
return deepMerge(
|
||||
newOptions.common,
|
||||
{ ...newOption, autoLoadEntities: true } as any,
|
||||
'replace',
|
||||
) as TypeormOption;
|
||||
});
|
||||
return newOptions;
|
||||
};
|
@ -1,10 +1,14 @@
|
||||
import { DynamicModule, Module, Provider, Type } from '@nestjs/common';
|
||||
import { DynamicModule, Module, ModuleMetadata, Provider, Type } from '@nestjs/common';
|
||||
import { getDataSourceToken, TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
|
||||
import { DataSource, ObjectType } from 'typeorm';
|
||||
|
||||
import { CUSTOM_REPOSITORY_METADATA } from '@/modules/database/constants';
|
||||
|
||||
import { Configure } from '../config/configure';
|
||||
|
||||
import { panic } from '../core/helpers';
|
||||
|
||||
import {
|
||||
DataExistConstraint,
|
||||
TreeUniqueConstraint,
|
||||
@ -12,21 +16,31 @@ import {
|
||||
UniqueConstraint,
|
||||
UniqueExistConstraint,
|
||||
} from './constraints';
|
||||
import { DBOptions } from './types';
|
||||
|
||||
@Module({})
|
||||
export class DatabaseModule {
|
||||
static forRoot(configRegister: () => TypeOrmModuleOptions): DynamicModule {
|
||||
static async forRoot(configure: Configure): Promise<DynamicModule> {
|
||||
if (!configure.has('database')) {
|
||||
panic({ message: 'Database config not exists' });
|
||||
}
|
||||
const { connections } = await configure.get<DBOptions>('database');
|
||||
const imports: ModuleMetadata['imports'] = [];
|
||||
for (const connection of connections) {
|
||||
imports.push(TypeOrmModule.forRoot(connection as TypeOrmModuleOptions));
|
||||
}
|
||||
const providers: ModuleMetadata['providers'] = [
|
||||
DataExistConstraint,
|
||||
UniqueConstraint,
|
||||
UniqueExistConstraint,
|
||||
TreeUniqueConstraint,
|
||||
TreeUniqueExistContraint,
|
||||
];
|
||||
return {
|
||||
global: true,
|
||||
module: DatabaseModule,
|
||||
imports: [TypeOrmModule.forRoot(configRegister())],
|
||||
providers: [
|
||||
DataExistConstraint,
|
||||
UniqueConstraint,
|
||||
UniqueExistConstraint,
|
||||
TreeUniqueConstraint,
|
||||
TreeUniqueExistContraint,
|
||||
],
|
||||
imports,
|
||||
providers,
|
||||
};
|
||||
}
|
||||
static forRepository<T extends Type<any>>(
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
import {
|
||||
FindTreeOptions,
|
||||
ObjectLiteral,
|
||||
@ -67,3 +68,15 @@ export type RepositoryType<T extends ObjectLiteral> =
|
||||
| TreeRepository<T>
|
||||
| BaseRepository<T>
|
||||
| BaseTreeRepository<T>;
|
||||
|
||||
export type DBConfig = {
|
||||
common: RecordAny;
|
||||
connections: Array<TypeOrmModuleOptions & { name?: string }>;
|
||||
};
|
||||
|
||||
export type TypeormOption = Omit<TypeOrmModuleOptions, 'name' | 'migrations'> & { name: string };
|
||||
|
||||
export type DBOptions = RecordAny & {
|
||||
common: RecordAny;
|
||||
connections: TypeormOption[];
|
||||
};
|
||||
|
11
src/modules/meilisearch/config.ts
Normal file
11
src/modules/meilisearch/config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ConfigureFactory, ConfigureRegister } from '../config/types';
|
||||
import { createConnectionOptions } from '../config/utils';
|
||||
|
||||
import { MeiliConfig } from './types';
|
||||
|
||||
export const createMeiliConfig: (
|
||||
registre: ConfigureRegister<RePartial<MeiliConfig>>,
|
||||
) => ConfigureFactory<MeiliConfig, MeiliConfig> = (register) => ({
|
||||
register,
|
||||
hook: (configure, value) => createConnectionOptions(value),
|
||||
});
|
@ -1,9 +0,0 @@
|
||||
import { MeiliConfig } from '@/modules/meilisearch/types';
|
||||
|
||||
export const MEILI_CONFIG = (): MeiliConfig => [
|
||||
{
|
||||
name: 'default',
|
||||
host: 'http://localhost:7700',
|
||||
apiKey: 'masterKey',
|
||||
},
|
||||
];
|
@ -1,12 +1,16 @@
|
||||
import { DynamicModule, Module } from '@nestjs/common';
|
||||
|
||||
import { MeiliService } from '@/modules/meilisearch/meili.service';
|
||||
import { MeiliConfig } from '@/modules/meilisearch/types';
|
||||
import { createMeiliOptions } from '@/modules/meilisearch/utils';
|
||||
|
||||
import { Configure } from '../config/configure';
|
||||
import { panic } from '../core/helpers';
|
||||
|
||||
@Module({})
|
||||
export class MeiliModule {
|
||||
static forRoot(configRegister: () => MeiliConfig): DynamicModule {
|
||||
static forRoot(configure: Configure): DynamicModule {
|
||||
if (!configure.has('meili')) {
|
||||
panic({ message: 'MeilliSearch config not exists' });
|
||||
}
|
||||
return {
|
||||
global: true,
|
||||
module: MeiliModule,
|
||||
@ -14,9 +18,7 @@ export class MeiliModule {
|
||||
{
|
||||
provide: MeiliService,
|
||||
useFactory: async () => {
|
||||
const service = new MeiliService(
|
||||
await createMeiliOptions(configRegister()),
|
||||
);
|
||||
const service = new MeiliService(await configure.get('meili'));
|
||||
await service.createClients();
|
||||
return service;
|
||||
},
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { MeiliConfig } from '@/modules/meilisearch/types';
|
||||
|
||||
export const createMeiliOptions = async (config: MeiliConfig): Promise<MeiliConfig | undefined> => {
|
||||
if (config.length < 0) {
|
||||
return config;
|
||||
}
|
||||
let options: MeiliConfig = [...config];
|
||||
const names = options.map(({ name }) => name);
|
||||
if (!names.includes('default')) {
|
||||
options[0].name = 'default';
|
||||
} else if (names.filter((name) => name === 'default').length > 0) {
|
||||
options = options.reduce(
|
||||
(o, n) => (o.map(({ name }) => name).includes('default') ? o : [...o, n]),
|
||||
[],
|
||||
);
|
||||
}
|
||||
return options;
|
||||
};
|
32
src/options.ts
Normal file
32
src/options.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
|
||||
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
|
||||
import * as configs from './config';
|
||||
import { ContentModule } from './modules/content/content.module';
|
||||
import { CoreModule } from './modules/core/core.module';
|
||||
import { CreateOptions } from './modules/core/types';
|
||||
import { DatabaseModule } from './modules/database/database.module';
|
||||
import { MeiliModule } from './modules/meilisearch/meili.module';
|
||||
|
||||
export const createOptions: CreateOptions = {
|
||||
config: { factories: configs as any, storage: { enable: true } },
|
||||
modules: async (configure) => [
|
||||
DatabaseModule.forRoot(configure),
|
||||
MeiliModule.forRoot(configure),
|
||||
ContentModule.forRoot(configure),
|
||||
CoreModule.forRoot(configure),
|
||||
],
|
||||
globals: {},
|
||||
builder: async ({ configure, BootModule }) => {
|
||||
const container = await NestFactory.create<NestFastifyApplication>(
|
||||
BootModule,
|
||||
new FastifyAdapter(),
|
||||
{
|
||||
cors: true,
|
||||
logger: ['error', 'warn'],
|
||||
},
|
||||
);
|
||||
return container;
|
||||
},
|
||||
};
|
@ -7,7 +7,6 @@ import { useContainer } from 'class-validator';
|
||||
import { isNil, pick } from 'lodash';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { AppModule } from '@/app.module';
|
||||
import { CategoryEntity, CommentEntity, PostEntity, TagEntity } from '@/modules/content/entities';
|
||||
import {
|
||||
CategoryRepository,
|
||||
@ -16,6 +15,7 @@ import {
|
||||
TagRepository,
|
||||
} from '@/modules/content/repositories';
|
||||
|
||||
import { CoreModule } from '@/modules/core/core.module';
|
||||
import { MeiliService } from '@/modules/meilisearch/meili.service';
|
||||
|
||||
import { generateRandomNumber, generateUniqueRandomNumbers } from './generate-mock-data';
|
||||
@ -37,10 +37,10 @@ describe('nest app test', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
imports: [CoreModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication<NestFastifyApplication>(new FastifyAdapter());
|
||||
useContainer(app.select(AppModule), { fallbackOnErrors: true });
|
||||
useContainer(app.select(CoreModule), { fallbackOnErrors: true });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
|
@ -1,24 +1,22 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import request from 'supertest';
|
||||
|
||||
import { CoreModule } from '@/modules/core/core.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [CoreModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user