mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
feat: add language crud
This commit is contained in:
151
api/src/i18n/controllers/language.controller.ts
Normal file
151
api/src/i18n/controllers/language.controller.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { CsrfCheck } from '@tekuconcept/nestjs-csrf';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { CsrfInterceptor } from '@/interceptors/csrf.interceptor';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseController } from '@/utils/generics/base-controller';
|
||||
import { DeleteResult } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
import { PageQueryPipe } from '@/utils/pagination/pagination-query.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { LanguageCreateDto, LanguageUpdateDto } from '../dto/language.dto';
|
||||
import { Language } from '../schemas/language.schema';
|
||||
import { LanguageService } from '../services/language.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('language')
|
||||
export class LanguageController extends BaseController<Language> {
|
||||
constructor(
|
||||
private readonly languageService: LanguageService,
|
||||
private readonly logger: LoggerService,
|
||||
) {
|
||||
super(languageService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of categories based on provided filters and pagination settings.
|
||||
* @param pageQuery - The pagination settings.
|
||||
* @param filters - The filters to apply to the language search.
|
||||
* @returns A Promise that resolves to a paginated list of categories.
|
||||
*/
|
||||
@Get()
|
||||
async findPage(
|
||||
@Query(PageQueryPipe) pageQuery: PageQueryDto<Language>,
|
||||
@Query(new SearchFilterPipe<Language>({ allowedFields: ['title', 'code'] }))
|
||||
filters: TFilterQuery<Language>,
|
||||
) {
|
||||
return await this.languageService.findPage(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the filtered number of categories.
|
||||
* @returns A promise that resolves to an object representing the filtered number of categories.
|
||||
*/
|
||||
@Get('count')
|
||||
async filterCount(
|
||||
@Query(
|
||||
new SearchFilterPipe<Language>({
|
||||
allowedFields: ['title', 'code'],
|
||||
}),
|
||||
)
|
||||
filters?: TFilterQuery<Language>,
|
||||
) {
|
||||
return await this.count(filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a language by its ID.
|
||||
* @param id - The ID of the language to find.
|
||||
* @returns A Promise that resolves to the found language.
|
||||
*/
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string): Promise<Language> {
|
||||
const doc = await this.languageService.findOne(id);
|
||||
if (!doc) {
|
||||
this.logger.warn(`Unable to find Language by id ${id}`);
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new language.
|
||||
* @param language - The data of the language to be created.
|
||||
* @returns A Promise that resolves to the created language.
|
||||
*/
|
||||
@CsrfCheck(true)
|
||||
@Post()
|
||||
async create(@Body() language: LanguageCreateDto): Promise<Language> {
|
||||
return await this.languageService.create(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing language.
|
||||
* @param id - The ID of the language to be updated.
|
||||
* @param languageUpdate - The updated data for the language.
|
||||
* @returns A Promise that resolves to the updated language.
|
||||
*/
|
||||
@CsrfCheck(true)
|
||||
@Patch(':id')
|
||||
async updateOne(
|
||||
@Param('id') id: string,
|
||||
@Body() languageUpdate: LanguageUpdateDto,
|
||||
): Promise<Language> {
|
||||
if ('default' in languageUpdate) {
|
||||
if (languageUpdate.default) {
|
||||
// A new default language is define, make sure that only one is marked as default
|
||||
await this.languageService.updateMany({}, { default: false });
|
||||
} else {
|
||||
throw new BadRequestException('Should not be able to disable default');
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.languageService.updateOne(id, languageUpdate);
|
||||
if (!result) {
|
||||
this.logger.warn(`Unable to update Language by id ${id}`);
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a language by its ID.
|
||||
* @param id - The ID of the language to be deleted.
|
||||
* @returns A Promise that resolves to the deletion result.
|
||||
*/
|
||||
@CsrfCheck(true)
|
||||
@Delete(':id')
|
||||
@HttpCode(204)
|
||||
async deleteOne(@Param('id') id: string): Promise<DeleteResult> {
|
||||
const result = await this.languageService.deleteOne(id);
|
||||
if (result.deletedCount === 0) {
|
||||
this.logger.warn(`Unable to delete Language by id ${id}`);
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
31
api/src/i18n/dto/language.dto.ts
Normal file
31
api/src/i18n/dto/language.dto.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class LanguageCreateDto {
|
||||
@ApiProperty({ description: 'Language Title', type: String })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: 'Language Code', type: String })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
code: string;
|
||||
|
||||
@ApiProperty({ description: 'Is Default Language ?', type: Boolean })
|
||||
@IsNotEmpty()
|
||||
@IsBoolean()
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
export class LanguageUpdateDto extends PartialType(LanguageCreateDto) {}
|
||||
@@ -7,23 +7,36 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { DynamicModule, Global, Inject, Module } from '@nestjs/common';
|
||||
import {
|
||||
DynamicModule,
|
||||
forwardRef,
|
||||
Global,
|
||||
Inject,
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { HttpAdapterHost } from '@nestjs/core';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import {
|
||||
I18N_OPTIONS,
|
||||
I18N_TRANSLATIONS,
|
||||
I18nModule as NativeI18nModule,
|
||||
I18nOptions,
|
||||
I18nTranslation,
|
||||
I18nModule as NativeI18nModule,
|
||||
} from 'nestjs-i18n';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { ChatModule } from '@/chat/chat.module';
|
||||
|
||||
import { LanguageController } from './controllers/language.controller';
|
||||
import { TranslationController } from './controllers/translation.controller';
|
||||
import { LanguageRepository } from './repositories/language.repository';
|
||||
import { TranslationRepository } from './repositories/translation.repository';
|
||||
import { LanguageModel } from './schemas/language.schema';
|
||||
import { TranslationModel } from './schemas/translation.schema';
|
||||
import { LanguageSeeder } from './seeds/language.seed';
|
||||
import { TranslationSeeder } from './seeds/translation.seed';
|
||||
import { I18nService } from './services/i18n.service';
|
||||
import { LanguageService } from './services/language.service';
|
||||
import { TranslationService } from './services/translation.service';
|
||||
|
||||
@Global()
|
||||
@@ -43,10 +56,19 @@ export class I18nModule extends NativeI18nModule {
|
||||
const { imports, providers, controllers, exports } = super.forRoot(options);
|
||||
return {
|
||||
module: I18nModule,
|
||||
imports: imports.concat([MongooseModule.forFeature([TranslationModel])]),
|
||||
controllers: controllers.concat([TranslationController]),
|
||||
imports: (imports || []).concat([
|
||||
MongooseModule.forFeature([LanguageModel, TranslationModel]),
|
||||
forwardRef(() => ChatModule),
|
||||
]),
|
||||
controllers: (controllers || []).concat([
|
||||
LanguageController,
|
||||
TranslationController,
|
||||
]),
|
||||
providers: providers.concat([
|
||||
I18nService,
|
||||
LanguageRepository,
|
||||
LanguageService,
|
||||
LanguageSeeder,
|
||||
TranslationRepository,
|
||||
TranslationService,
|
||||
TranslationSeeder,
|
||||
|
||||
23
api/src/i18n/repositories/language.repository.ts
Normal file
23
api/src/i18n/repositories/language.repository.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
|
||||
import { Language } from '../schemas/language.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageRepository extends BaseRepository<Language> {
|
||||
constructor(@InjectModel(Language.name) readonly model: Model<Language>) {
|
||||
super(model, Language);
|
||||
}
|
||||
}
|
||||
44
api/src/i18n/schemas/language.schema.ts
Normal file
44
api/src/i18n/schemas/language.schema.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Language extends BaseSchema {
|
||||
@Prop({
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
})
|
||||
title: string;
|
||||
|
||||
@Prop({
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
})
|
||||
code: string;
|
||||
|
||||
@Prop({
|
||||
type: Boolean,
|
||||
})
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
export const LanguageModel: ModelDefinition = {
|
||||
name: Language.name,
|
||||
schema: SchemaFactory.createForClass(Language),
|
||||
};
|
||||
|
||||
export type LanguageDocument = THydratedDocument<Language>;
|
||||
|
||||
export default LanguageModel.schema;
|
||||
23
api/src/i18n/seeds/language.seed-model.ts
Normal file
23
api/src/i18n/seeds/language.seed-model.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { LanguageCreateDto } from '../dto/language.dto';
|
||||
|
||||
export const languageModels: LanguageCreateDto[] = [
|
||||
{
|
||||
title: 'English',
|
||||
code: 'en',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
title: 'Français',
|
||||
code: 'fr',
|
||||
default: false,
|
||||
},
|
||||
];
|
||||
22
api/src/i18n/seeds/language.seed.ts
Normal file
22
api/src/i18n/seeds/language.seed.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { LanguageRepository } from '../repositories/language.repository';
|
||||
import { Language } from '../schemas/language.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageSeeder extends BaseSeeder<Language> {
|
||||
constructor(private readonly languageRepository: LanguageRepository) {
|
||||
super(languageRepository);
|
||||
}
|
||||
}
|
||||
22
api/src/i18n/services/language.service.ts
Normal file
22
api/src/i18n/services/language.service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
|
||||
import { LanguageRepository } from '../repositories/language.repository';
|
||||
import { Language } from '../schemas/language.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageService extends BaseService<Language> {
|
||||
constructor(readonly repository: LanguageRepository) {
|
||||
super(repository);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import { CategorySeeder } from './chat/seeds/category.seed';
|
||||
import { categoryModels } from './chat/seeds/category.seed-model';
|
||||
import { ContextVarSeeder } from './chat/seeds/context-var.seed';
|
||||
import { contextVarModels } from './chat/seeds/context-var.seed-model';
|
||||
import { LanguageSeeder } from './i18n/seeds/language.seed';
|
||||
import { languageModels } from './i18n/seeds/language.seed-model';
|
||||
import { TranslationSeeder } from './i18n/seeds/translation.seed';
|
||||
import { translationModels } from './i18n/seeds/translation.seed-model';
|
||||
import { LoggerService } from './logger/logger.service';
|
||||
@@ -40,6 +42,7 @@ export async function seedDatabase(app: INestApplicationContext) {
|
||||
const settingSeeder = app.get(SettingSeeder);
|
||||
const permissionSeeder = app.get(PermissionSeeder);
|
||||
const userSeeder = app.get(UserSeeder);
|
||||
const languageSeeder = app.get(LanguageSeeder);
|
||||
const translationSeeder = app.get(TranslationSeeder);
|
||||
const nlpEntitySeeder = app.get(NlpEntitySeeder);
|
||||
const nlpValueSeeder = app.get(NlpValueSeeder);
|
||||
@@ -127,6 +130,14 @@ export async function seedDatabase(app: INestApplicationContext) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Seed languages
|
||||
try {
|
||||
await languageSeeder.seed(languageModels);
|
||||
} catch (e) {
|
||||
logger.error('Unable to seed the database with languages!');
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Seed translations
|
||||
try {
|
||||
await translationSeeder.seed(translationModels);
|
||||
|
||||
@@ -100,6 +100,11 @@ export const modelModels: ModelCreateDto[] = [
|
||||
identity: 'subscriber',
|
||||
attributes: {},
|
||||
},
|
||||
{
|
||||
name: 'Language',
|
||||
identity: 'language',
|
||||
attributes: {},
|
||||
},
|
||||
{
|
||||
name: 'Translation',
|
||||
identity: 'translation',
|
||||
|
||||
@@ -26,6 +26,7 @@ export type TModel =
|
||||
| 'conversation'
|
||||
| 'message'
|
||||
| 'subscriber'
|
||||
| 'language'
|
||||
| 'translation'
|
||||
| 'botstats'
|
||||
| 'menu'
|
||||
|
||||
Reference in New Issue
Block a user