add db migration
This commit is contained in:
parent
67e4466687
commit
13b5bc629a
@ -9,12 +9,15 @@ import { SearchService } from '@/modules/content/services';
|
|||||||
import { SanitizeService } from '@/modules/content/services/SanitizeService';
|
import { SanitizeService } from '@/modules/content/services/SanitizeService';
|
||||||
|
|
||||||
import { PostService } from '@/modules/content/services/post.service';
|
import { PostService } from '@/modules/content/services/post.service';
|
||||||
import { PostSubscriber } from '@/modules/content/subscribers/post.subscriber';
|
|
||||||
import { DatabaseModule } from '@/modules/database/database.module';
|
import { DatabaseModule } from '@/modules/database/database.module';
|
||||||
|
|
||||||
|
import { addSubscribers } from '@/modules/database/utils';
|
||||||
|
|
||||||
import { Configure } from '../config/configure';
|
import { Configure } from '../config/configure';
|
||||||
|
|
||||||
import { defauleContentConfig } from './config';
|
import { defauleContentConfig } from './config';
|
||||||
|
import * as subscribers from './subscribers';
|
||||||
import { ContentConfig } from './types';
|
import { ContentConfig } from './types';
|
||||||
|
|
||||||
@Module({})
|
@Module({})
|
||||||
@ -23,7 +26,7 @@ export class ContentModule {
|
|||||||
const config = await configure.get<ContentConfig>('content', defauleContentConfig);
|
const config = await configure.get<ContentConfig>('content', defauleContentConfig);
|
||||||
const providers: ModuleMetadata['providers'] = [
|
const providers: ModuleMetadata['providers'] = [
|
||||||
...Object.values(services),
|
...Object.values(services),
|
||||||
PostSubscriber,
|
...(await addSubscribers(configure, Object.values(subscribers))),
|
||||||
{
|
{
|
||||||
provide: PostService,
|
provide: PostService,
|
||||||
inject: [
|
inject: [
|
||||||
|
1
src/modules/content/subscribers/index.ts
Normal file
1
src/modules/content/subscribers/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './post.subscriber';
|
@ -1,33 +1,33 @@
|
|||||||
import { Optional } from '@nestjs/common';
|
|
||||||
import { isNil } from 'lodash';
|
import { isNil } from 'lodash';
|
||||||
import { DataSource, EventSubscriber, ObjectType } from 'typeorm';
|
import { DataSource, EventSubscriber, ObjectType } from 'typeorm';
|
||||||
|
|
||||||
import { Configure } from '@/modules/config/configure';
|
import { Configure } from '@/modules/config/configure';
|
||||||
import { PostBodyType } from '@/modules/content/constants';
|
import { PostBodyType } from '@/modules/content/constants';
|
||||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||||
import { PostRepository } from '@/modules/content/repositories/post.repository';
|
|
||||||
import { SanitizeService } from '@/modules/content/services/SanitizeService';
|
import { SanitizeService } from '@/modules/content/services/SanitizeService';
|
||||||
import { BaseSubscriber } from '@/modules/database/base/subscriber';
|
import { BaseSubscriber } from '@/modules/database/base/subscriber';
|
||||||
|
|
||||||
@EventSubscriber()
|
@EventSubscriber()
|
||||||
export class PostSubscriber extends BaseSubscriber<PostEntity> {
|
export class PostSubscriber extends BaseSubscriber<PostEntity> {
|
||||||
protected entity: ObjectType<PostEntity> = PostEntity;
|
protected entity: ObjectType<PostEntity> = PostEntity;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected dataSource: DataSource,
|
protected dataSource: DataSource,
|
||||||
protected postRepository: PostRepository,
|
protected _configure: Configure,
|
||||||
protected configure: Configure,
|
|
||||||
@Optional() protected sanitizeService: SanitizeService,
|
|
||||||
) {
|
) {
|
||||||
super(dataSource);
|
super(dataSource, _configure);
|
||||||
|
}
|
||||||
|
|
||||||
|
get configure(): Configure {
|
||||||
|
return this._configure;
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterLoad(entity: PostEntity) {
|
async afterLoad(entity: PostEntity) {
|
||||||
if (
|
const sanitizeService = (await this.configure.get('content.htmlEnabled'))
|
||||||
(await this.configure.get('content.htmlEnabled')) &&
|
? this.container.get(SanitizeService)
|
||||||
!isNil(this.sanitizeService) &&
|
: undefined;
|
||||||
entity.type === PostBodyType.HTML
|
if (!isNil(sanitizeService) && entity.type === PostBodyType.HTML) {
|
||||||
) {
|
entity.body = sanitizeService.sanitize(entity.body);
|
||||||
entity.body = this.sanitizeService.sanitize(entity.body);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,8 +76,14 @@ export async function panic(option: PanicOption | string) {
|
|||||||
console.log(chalk.red(`\n❌ ${option}`));
|
console.log(chalk.red(`\n❌ ${option}`));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
const { error, message, exit = true } = option;
|
const { error, message, spinner, exit = true } = option;
|
||||||
isNil(error) ? console.log(chalk.red(`\n❌ ${message}`)) : console.log(chalk.red(error));
|
if (isNil(error)) {
|
||||||
|
isNil(spinner)
|
||||||
|
? console.log(chalk.red(`\n❌ ${message}`))
|
||||||
|
: spinner.succeed(chalk.red(`\n❌ ${message}`));
|
||||||
|
} else {
|
||||||
|
isNil(spinner) ? console.log(chalk.red(error)) : spinner.fail(chalk.red(error));
|
||||||
|
}
|
||||||
if (exit) {
|
if (exit) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Optional } from '@nestjs/common';
|
import { Optional } from '@nestjs/common';
|
||||||
|
import { NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||||
import { isNil } from 'lodash';
|
import { isNil } from 'lodash';
|
||||||
import {
|
import {
|
||||||
DataSource,
|
DataSource,
|
||||||
@ -17,6 +18,10 @@ import {
|
|||||||
UpdateEvent,
|
UpdateEvent,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import { Configure } from '@/modules/config/configure';
|
||||||
|
|
||||||
|
import { app } from '@/modules/core/helpers/app';
|
||||||
|
|
||||||
import { RepositoryType } from '../types';
|
import { RepositoryType } from '../types';
|
||||||
import { getCustomRepository } from '../utils';
|
import { getCustomRepository } from '../utils';
|
||||||
|
|
||||||
@ -36,12 +41,25 @@ export abstract class BaseSubscriber<T extends ObjectLiteral>
|
|||||||
{
|
{
|
||||||
protected abstract entity: ObjectType<T>;
|
protected abstract entity: ObjectType<T>;
|
||||||
|
|
||||||
protected constructor(@Optional() protected dataSource?: DataSource) {
|
protected constructor(
|
||||||
|
@Optional() protected dataSource?: DataSource,
|
||||||
|
@Optional() protected _configure?: Configure,
|
||||||
|
) {
|
||||||
if (!isNil(this.dataSource)) {
|
if (!isNil(this.dataSource)) {
|
||||||
this.dataSource.subscribers.push(this);
|
this.dataSource.subscribers.push(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get configure() {
|
||||||
|
return isNil(this._configure)
|
||||||
|
? this.container.get(Configure, { strict: false })
|
||||||
|
: this._configure;
|
||||||
|
}
|
||||||
|
|
||||||
|
get container(): NestFastifyApplication {
|
||||||
|
return app.container;
|
||||||
|
}
|
||||||
|
|
||||||
protected getDataSource(event: SubscriberEvent<T>) {
|
protected getDataSource(event: SubscriberEvent<T>) {
|
||||||
return this.dataSource ?? event.connection;
|
return this.dataSource ?? event.connection;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
import { ConfigureFactory, ConfigureRegister } from '../config/types';
|
import { ConfigureFactory, ConfigureRegister } from '../config/types';
|
||||||
import { createConnectionOptions } from '../config/utils';
|
import { createConnectionOptions } from '../config/utils';
|
||||||
import { deepMerge } from '../core/helpers';
|
import { deepMerge } from '../core/helpers';
|
||||||
@ -18,7 +20,11 @@ export const createDBConfig: (
|
|||||||
export const createDBOptions = (options: DBConfig) => {
|
export const createDBOptions = (options: DBConfig) => {
|
||||||
const newOptions: DBOptions = {
|
const newOptions: DBOptions = {
|
||||||
common: deepMerge(
|
common: deepMerge(
|
||||||
{ charset: 'utf8mb4', logging: ['error'] },
|
{
|
||||||
|
charset: 'utf8mb4',
|
||||||
|
logging: ['error'],
|
||||||
|
paths: { migration: resolve(__dirname, '../../database/migrations') },
|
||||||
|
},
|
||||||
options.common ?? {},
|
options.common ?? {},
|
||||||
'replace',
|
'replace',
|
||||||
),
|
),
|
||||||
@ -29,7 +35,7 @@ export const createDBOptions = (options: DBConfig) => {
|
|||||||
const newOption = { ...connection, entities };
|
const newOption = { ...connection, entities };
|
||||||
return deepMerge(
|
return deepMerge(
|
||||||
newOptions.common,
|
newOptions.common,
|
||||||
{ ...newOption, autoLoadEntities: true } as any,
|
{ ...newOption, autoLoadEntities: true, synchronize: false } as any,
|
||||||
'replace',
|
'replace',
|
||||||
) as TypeormOption;
|
) as TypeormOption;
|
||||||
});
|
});
|
||||||
|
@ -2,8 +2,8 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
|||||||
import {
|
import {
|
||||||
FindTreeOptions,
|
FindTreeOptions,
|
||||||
ObjectLiteral,
|
ObjectLiteral,
|
||||||
SelectQueryBuilder,
|
|
||||||
Repository,
|
Repository,
|
||||||
|
SelectQueryBuilder,
|
||||||
TreeRepository,
|
TreeRepository,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
@ -70,13 +70,21 @@ export type RepositoryType<T extends ObjectLiteral> =
|
|||||||
| BaseTreeRepository<T>;
|
| BaseTreeRepository<T>;
|
||||||
|
|
||||||
export type DBConfig = {
|
export type DBConfig = {
|
||||||
common: RecordAny;
|
common: RecordAny & DBAdditionalOption;
|
||||||
connections: Array<TypeOrmModuleOptions & { name?: string }>;
|
connections: Array<TypeOrmModuleOptions & { name?: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TypeormOption = Omit<TypeOrmModuleOptions, 'name' | 'migrations'> & { name: string };
|
export type TypeormOption = Omit<TypeOrmModuleOptions, 'name' | 'migrations'> & {
|
||||||
|
name: string;
|
||||||
|
} & DBAdditionalOption;
|
||||||
|
|
||||||
export type DBOptions = RecordAny & {
|
export type DBOptions = RecordAny & {
|
||||||
common: RecordAny;
|
common: RecordAny;
|
||||||
connections: TypeormOption[];
|
connections: TypeormOption[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DBAdditionalOption = {
|
||||||
|
path?: {
|
||||||
|
migration?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
|
import { Type } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { EntityClassOrSchema } from '@nestjs/typeorm/dist/interfaces/entity-class-or-schema.type';
|
||||||
import { isArray, isNil } from 'lodash';
|
import { isArray, isNil } from 'lodash';
|
||||||
import { DataSource, ObjectLiteral, ObjectType, Repository, SelectQueryBuilder } from 'typeorm';
|
import { DataSource, ObjectLiteral, ObjectType, Repository, SelectQueryBuilder } from 'typeorm';
|
||||||
|
|
||||||
import { OrderQueryType, PaginateOptions, PaginateReturn } from '@/modules/database/types';
|
import { Configure } from '@/modules/config/configure';
|
||||||
|
import {
|
||||||
|
DBOptions,
|
||||||
|
OrderQueryType,
|
||||||
|
PaginateOptions,
|
||||||
|
PaginateReturn,
|
||||||
|
} from '@/modules/database/types';
|
||||||
|
|
||||||
import { CUSTOM_REPOSITORY_METADATA } from './constants';
|
import { CUSTOM_REPOSITORY_METADATA } from './constants';
|
||||||
|
|
||||||
@ -97,3 +106,48 @@ export const getCustomRepository = <P extends Repository<T>, T extends ObjectLit
|
|||||||
const base = dataSource.getRepository<ObjectType<any>>(entity);
|
const base = dataSource.getRepository<ObjectType<any>>(entity);
|
||||||
return new Repo(base.target, base.manager, base.queryRunner) as P;
|
return new Repo(base.target, base.manager, base.queryRunner) as P;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addEntities = async (
|
||||||
|
configure: Configure,
|
||||||
|
entities: EntityClassOrSchema[] = [],
|
||||||
|
dataSource = 'default',
|
||||||
|
) => {
|
||||||
|
const database = await configure.get<DBOptions>('database');
|
||||||
|
if (isNil(database)) {
|
||||||
|
throw new Error('Database not exists');
|
||||||
|
}
|
||||||
|
const dbConfig = database.connections.find(({ name }) => name === dataSource);
|
||||||
|
if (isNil(dbConfig)) {
|
||||||
|
throw new Error(`Database connection ${dataSource} not exists`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldEntities = (dbConfig.entities ?? []) as ObjectLiteral[];
|
||||||
|
const newEntities = database.connections.map((conn) =>
|
||||||
|
conn.name === dataSource ? { ...conn, entities: [...oldEntities, ...entities] } : conn,
|
||||||
|
);
|
||||||
|
configure.set('database.connections', newEntities);
|
||||||
|
return TypeOrmModule.forFeature(entities, dataSource);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function addSubscribers(
|
||||||
|
configure: Configure,
|
||||||
|
subscribers: Type<any>[] = [],
|
||||||
|
dataSource = 'default',
|
||||||
|
) {
|
||||||
|
const database = await configure.get<DBOptions>('database');
|
||||||
|
if (isNil(database)) {
|
||||||
|
throw new Error('Database not exists');
|
||||||
|
}
|
||||||
|
const dbConfig = database.connections.find(({ name }) => name === dataSource);
|
||||||
|
if (isNil(dbConfig)) {
|
||||||
|
throw new Error(`Database connection ${dataSource} not exists`);
|
||||||
|
}
|
||||||
|
const oldSubscribers = (dbConfig.subscribers ?? []) as any[];
|
||||||
|
const newSubscribers = database.connections.map((conn) =>
|
||||||
|
conn.name === dataSource
|
||||||
|
? { ...conn, subscribers: [...oldSubscribers, subscribers] }
|
||||||
|
: conn,
|
||||||
|
);
|
||||||
|
configure.set('database.connections', newSubscribers);
|
||||||
|
return subscribers;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user