add db migration

This commit is contained in:
liuyi 2025-06-20 11:06:39 +08:00
parent 67e4466687
commit 13b5bc629a
8 changed files with 119 additions and 23 deletions

View File

@ -9,12 +9,15 @@ import { SearchService } from '@/modules/content/services';
import { SanitizeService } from '@/modules/content/services/SanitizeService';
import { PostService } from '@/modules/content/services/post.service';
import { PostSubscriber } from '@/modules/content/subscribers/post.subscriber';
import { DatabaseModule } from '@/modules/database/database.module';
import { addSubscribers } from '@/modules/database/utils';
import { Configure } from '../config/configure';
import { defauleContentConfig } from './config';
import * as subscribers from './subscribers';
import { ContentConfig } from './types';
@Module({})
@ -23,7 +26,7 @@ export class ContentModule {
const config = await configure.get<ContentConfig>('content', defauleContentConfig);
const providers: ModuleMetadata['providers'] = [
...Object.values(services),
PostSubscriber,
...(await addSubscribers(configure, Object.values(subscribers))),
{
provide: PostService,
inject: [

View File

@ -0,0 +1 @@
export * from './post.subscriber';

View File

@ -1,33 +1,33 @@
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';
import { SanitizeService } from '@/modules/content/services/SanitizeService';
import { BaseSubscriber } from '@/modules/database/base/subscriber';
@EventSubscriber()
export class PostSubscriber extends BaseSubscriber<PostEntity> {
protected entity: ObjectType<PostEntity> = PostEntity;
constructor(
protected dataSource: DataSource,
protected postRepository: PostRepository,
protected configure: Configure,
@Optional() protected sanitizeService: SanitizeService,
protected _configure: Configure,
) {
super(dataSource);
super(dataSource, _configure);
}
get configure(): Configure {
return this._configure;
}
async afterLoad(entity: PostEntity) {
if (
(await this.configure.get('content.htmlEnabled')) &&
!isNil(this.sanitizeService) &&
entity.type === PostBodyType.HTML
) {
entity.body = this.sanitizeService.sanitize(entity.body);
const sanitizeService = (await this.configure.get('content.htmlEnabled'))
? this.container.get(SanitizeService)
: undefined;
if (!isNil(sanitizeService) && entity.type === PostBodyType.HTML) {
entity.body = sanitizeService.sanitize(entity.body);
}
}
}

View File

@ -76,8 +76,14 @@ export async function panic(option: PanicOption | 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));
const { error, message, spinner, exit = true } = option;
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) {
process.exit(1);
}

View File

@ -1,4 +1,5 @@
import { Optional } from '@nestjs/common';
import { NestFastifyApplication } from '@nestjs/platform-fastify';
import { isNil } from 'lodash';
import {
DataSource,
@ -17,6 +18,10 @@ import {
UpdateEvent,
} from 'typeorm';
import { Configure } from '@/modules/config/configure';
import { app } from '@/modules/core/helpers/app';
import { RepositoryType } from '../types';
import { getCustomRepository } from '../utils';
@ -36,12 +41,25 @@ export abstract class BaseSubscriber<T extends ObjectLiteral>
{
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)) {
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>) {
return this.dataSource ?? event.connection;
}

View File

@ -1,3 +1,5 @@
import { resolve } from 'path';
import { ConfigureFactory, ConfigureRegister } from '../config/types';
import { createConnectionOptions } from '../config/utils';
import { deepMerge } from '../core/helpers';
@ -18,7 +20,11 @@ export const createDBConfig: (
export const createDBOptions = (options: DBConfig) => {
const newOptions: DBOptions = {
common: deepMerge(
{ charset: 'utf8mb4', logging: ['error'] },
{
charset: 'utf8mb4',
logging: ['error'],
paths: { migration: resolve(__dirname, '../../database/migrations') },
},
options.common ?? {},
'replace',
),
@ -29,7 +35,7 @@ export const createDBOptions = (options: DBConfig) => {
const newOption = { ...connection, entities };
return deepMerge(
newOptions.common,
{ ...newOption, autoLoadEntities: true } as any,
{ ...newOption, autoLoadEntities: true, synchronize: false } as any,
'replace',
) as TypeormOption;
});

View File

@ -2,8 +2,8 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import {
FindTreeOptions,
ObjectLiteral,
SelectQueryBuilder,
Repository,
SelectQueryBuilder,
TreeRepository,
} from 'typeorm';
@ -70,13 +70,21 @@ export type RepositoryType<T extends ObjectLiteral> =
| BaseTreeRepository<T>;
export type DBConfig = {
common: RecordAny;
common: RecordAny & DBAdditionalOption;
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 & {
common: RecordAny;
connections: TypeormOption[];
};
type DBAdditionalOption = {
path?: {
migration?: string;
};
};

View File

@ -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 { 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';
@ -97,3 +106,48 @@ export const getCustomRepository = <P extends Repository<T>, T extends ObjectLit
const base = dataSource.getRepository<ObjectType<any>>(entity);
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;
}