Compare commits
	
		
			24 Commits
		
	
	
		
			main
			...
			anagrafica
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7970ccfc0e | |||
| aa07f3d4f9 | |||
| 2c313a0dd0 | |||
| 577f781556 | |||
| bfd25acd5b | |||
| d707e14a8b | |||
| d6ffa909aa | |||
| 0148082d0a | |||
| b180968def | |||
| 60b1701d01 | |||
| 580de7d81b | |||
| 1b7c586b69 | |||
| ef50d7075f | |||
| 3042e693be | |||
| 06d7e51f63 | |||
| 851153c8e1 | |||
| 2735fa4791 | |||
| cc6fe98e20 | |||
| 58de1b1652 | |||
| 0d60b73816 | |||
| 9f35377481 | |||
| a742b9dc5e | |||
| 8806114929 | |||
| a8596c8b4d | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,8 @@ | ||||
| # See http://help.github.com/ignore-files/ for more about ignoring files. | ||||
| 
 | ||||
| # dotenv | ||||
| .env | ||||
| 
 | ||||
| # compiled output | ||||
| dist | ||||
| tmp | ||||
| @ -40,3 +43,6 @@ Thumbs.db | ||||
| 
 | ||||
| .nx/cache | ||||
| .nx/workspace-data | ||||
| 
 | ||||
| # typeorm | ||||
| typeorm-model-generator-output/ | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| { | ||||
|   "singleQuote": true | ||||
|   "printWidth": 120, | ||||
|   "singleQuote": true, | ||||
|   "plugins": ["prettier-plugin-organize-imports"] | ||||
| } | ||||
|  | ||||
							
								
								
									
										20
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| { | ||||
|   // Use IntelliSense to learn about possible attributes. | ||||
|   // Hover to view descriptions of existing attributes. | ||||
|   // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|   "version": "0.2.0", | ||||
|   "configurations": [ | ||||
|     { | ||||
|       "name": "Attach to ebitemp-api", | ||||
|       "port": 9229, | ||||
|       "request": "attach", | ||||
|       "restart": true, | ||||
|       "skipFiles": [ | ||||
|         "<node_internals>/**", | ||||
|         "${workspaceFolder}/node_modules/**/*.js" | ||||
|       ], | ||||
|       "internalConsoleOptions": "neverOpen", | ||||
|       "type": "node" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										22
									
								
								apps/ebitemp-api/.tomg-config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								apps/ebitemp-api/.tomg-config
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| [ | ||||
|   { | ||||
|     "pluralizeNames": true, | ||||
|     "noConfigs": true, | ||||
|     "convertCaseFile": "none", | ||||
|     "convertCaseEntity": "pascal", | ||||
|     "convertCaseProperty": "camel", | ||||
|     "convertEol": "LF", | ||||
|     "propertyVisibility": "none", | ||||
|     "lazy": false, | ||||
|     "activeRecord": false, | ||||
|     "generateConstructor": true, | ||||
|     "customNamingStrategyPath": ".tomg-naming-strategy.js", | ||||
|     "relationIds": false, | ||||
|     "strictMode": "!", | ||||
|     "skipSchema": true, | ||||
|     "indexFile": false, | ||||
|     "exportType": "named", | ||||
|     "skipNonPrimaryKeyIndexes": true, | ||||
|     "removeColumnsInRelation": false | ||||
|   } | ||||
| ] | ||||
							
								
								
									
										39
									
								
								apps/ebitemp-api/.tomg-naming-strategy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								apps/ebitemp-api/.tomg-naming-strategy.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| /** | ||||
|  * @typedef {import('typeorm-model-generator').Column} Column | ||||
|  * @typedef {import('typeorm-model-generator').Entity} Entity | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Customizes the entity name. | ||||
|  * @param {string} oldEntityName - The default entity name. | ||||
|  * @param {Entity} entity - The entity. | ||||
|  * @returns {string} The new entity name. | ||||
|  */ | ||||
| function entityName(oldEntityName, entity) { | ||||
|   return oldEntityName + 'Entity'; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Customizes the column name. | ||||
|  * @param {string} oldColumnName - The default column name. | ||||
|  * @param {Column} column - The column. | ||||
|  * @returns {string} The new column name. | ||||
|  */ | ||||
| function columnName(oldColumnName, column) { | ||||
|   return oldColumnName; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Customizes the file name. | ||||
|  * @param {string} oldFileName - The default file name. | ||||
|  * @returns {string} The new file name. | ||||
|  */ | ||||
| function fileName(oldFileName) { | ||||
|   return oldFileName.replace('Entity', '.entity'); | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|   entityName, | ||||
|   columnName, | ||||
|   fileName, | ||||
| }; | ||||
| @ -1,5 +1,16 @@ | ||||
| import baseConfig from "../../eslint.config.mjs"; | ||||
| import tseslint from 'typescript-eslint'; | ||||
| 
 | ||||
| export default [ | ||||
|     ...baseConfig | ||||
| ]; | ||||
| export default tseslint.config([ | ||||
|     ...baseConfig, | ||||
|     { | ||||
|       files: ['src/**/*.ts', 'src/**/*.tsx'], | ||||
|       extends: [tseslint.configs.recommendedTypeCheckedOnly], | ||||
|       languageOptions: { | ||||
|         parserOptions: { | ||||
|           projectService: true, | ||||
|           tsconfigRootDir: import.meta.dirname, | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
| ]); | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import { Controller, Get } from '@nestjs/common'; | ||||
| import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common'; | ||||
| import { AppService } from './app.service'; | ||||
| import { Public } from '../modules/auth/strategies/jwt/jwt-auth.guard'; | ||||
| 
 | ||||
| @Controller() | ||||
| @Public() | ||||
| @Controller({ version: VERSION_NEUTRAL }) | ||||
| export class AppController { | ||||
|   constructor(private readonly appService: AppService) {} | ||||
| 
 | ||||
|   @Get() | ||||
|   getData() { | ||||
|     return this.appService.getData(); | ||||
|   async getData() { | ||||
|     return await this.appService.getData(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -2,8 +2,35 @@ import { Module } from '@nestjs/common'; | ||||
| import { AppController } from './app.controller'; | ||||
| import { AppService } from './app.service'; | ||||
| 
 | ||||
| import { AppAuthModule } from '../modules/auth/auth.module'; | ||||
| import { AppClsModule } from '../modules/cls/cls.module'; | ||||
| import { AppConfigModule } from '../modules/config/config.module'; | ||||
| import { AppDatabaseModule } from '../modules/database/database.module'; | ||||
| import { EnumifyModule } from '../modules/enumify/enumify.module'; | ||||
| import { AppFileTransactionsModule } from '../modules/file-transactions/file-transactions.module'; | ||||
| import { AppHealthModule } from '../modules/health/health.module'; | ||||
| import { AppLoggerModule } from '../modules/logger/logger.module'; | ||||
| import { AppRequestLoggingModule } from '../modules/request-logging/request-logging.module'; | ||||
| import { AppScheduleModule } from '../modules/schedule/schedule.module'; | ||||
| import { AppValidationModule } from '../modules/validation/validation.module'; | ||||
| 
 | ||||
| import { AnagraficaModule } from '../features/anagrafica/anagrafica.module'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [], | ||||
|   imports: [ | ||||
|     AppConfigModule, | ||||
|     AppLoggerModule, | ||||
|     AppDatabaseModule, | ||||
|     AppClsModule, | ||||
|     AppHealthModule, | ||||
|     AppFileTransactionsModule, | ||||
|     AppScheduleModule, | ||||
|     AppValidationModule, | ||||
|     AppAuthModule, | ||||
|     AppRequestLoggingModule, | ||||
|     EnumifyModule, | ||||
|     AnagraficaModule, | ||||
|   ], | ||||
|   controllers: [AppController], | ||||
|   providers: [AppService], | ||||
| }) | ||||
|  | ||||
| @ -1,8 +1,10 @@ | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Injectable, Logger } from '@nestjs/common'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class AppService { | ||||
|   getData(): { message: string } { | ||||
|   private readonly logger = new Logger(AppService.name); | ||||
| 
 | ||||
|   async getData() { | ||||
|     return ({ message: 'Hello API' }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,20 @@ | ||||
| import { Test } from '@nestjs/testing'; | ||||
| import { AnagraficaController } from './anagrafica.controller'; | ||||
| import { AnagraficaService } from './anagrafica.service'; | ||||
| 
 | ||||
| describe('AnagraficaController', () => { | ||||
|   let controller: AnagraficaController; | ||||
| 
 | ||||
|   beforeEach(async () => { | ||||
|     const module = await Test.createTestingModule({ | ||||
|       providers: [AnagraficaService], | ||||
|       controllers: [AnagraficaController], | ||||
|     }).compile(); | ||||
| 
 | ||||
|     controller = module.get(AnagraficaController); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be defined', () => { | ||||
|     expect(controller).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,28 @@ | ||||
| import { Controller, Get, Param, UseInterceptors } from '@nestjs/common'; | ||||
| import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; | ||||
| import { ApiOkPaginatedResponse, ApiPaginationQuery, Paginate, PaginateQuery } from 'nestjs-paginate'; | ||||
| import { SocioEntity } from '../../modules/database/connections/oceano'; | ||||
| import { AnagraficaService } from './anagrafica.service'; | ||||
| import { RequestLoggerInterceptor } from '../../modules/request-logging/request-logger.interceptor'; | ||||
| 
 | ||||
| @UseInterceptors(RequestLoggerInterceptor) | ||||
| @ApiBearerAuth() | ||||
| @ApiTags('anagrafica') | ||||
| @Controller('anagrafiche') | ||||
| export class AnagraficaController { | ||||
|   constructor(private anagraficaService: AnagraficaService) {} | ||||
| 
 | ||||
|   @Get() | ||||
|   @ApiOperation({ summary: 'Find all Anagrafiche' }) | ||||
|   @ApiPaginationQuery(AnagraficaService.findPaginateConfig()) | ||||
|   @ApiOkPaginatedResponse(SocioEntity, AnagraficaService.findPaginateConfig()) | ||||
|   async getAnagrafiche(@Paginate() query: PaginateQuery) { | ||||
|     return this.anagraficaService.getAnagrafiche(query); | ||||
|   } | ||||
| 
 | ||||
|   @Get('id/:id') | ||||
|   @ApiOperation({ summary: 'Get one Anagrafica by id' }) | ||||
|   async getOneAnagraficaById(@Param('id') id: string) { | ||||
|     return this.anagraficaService.getOneAnagraficaById(id); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { SocioEntity } from '../../modules/database/connections/oceano'; | ||||
| import { OCEANO_DATASOURCE } from '../../modules/database/connections/oceano/database.constants'; | ||||
| import { AnagraficaController } from './anagrafica.controller'; | ||||
| import { AnagraficaService } from './anagrafica.service'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [TypeOrmModule.forFeature([SocioEntity], OCEANO_DATASOURCE)], | ||||
|   controllers: [AnagraficaController], | ||||
|   providers: [AnagraficaService], | ||||
|   exports: [AnagraficaService], | ||||
| }) | ||||
| export class AnagraficaModule {} | ||||
| @ -0,0 +1,18 @@ | ||||
| import { Test } from '@nestjs/testing'; | ||||
| import { AnagraficaService } from './anagrafica.service'; | ||||
| 
 | ||||
| describe('AnagraficaService', () => { | ||||
|   let service: AnagraficaService; | ||||
| 
 | ||||
|   beforeEach(async () => { | ||||
|     const module = await Test.createTestingModule({ | ||||
|       providers: [AnagraficaService], | ||||
|     }).compile(); | ||||
| 
 | ||||
|     service = module.get(AnagraficaService); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be defined', () => { | ||||
|     expect(service).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,42 @@ | ||||
| import { Injectable, Logger } from '@nestjs/common'; | ||||
| import { InjectRepository } from '@nestjs/typeorm'; | ||||
| import { paginate, PaginateConfig, PaginateQuery } from 'nestjs-paginate'; | ||||
| import { Repository } from 'typeorm'; | ||||
| import { SocioEntity } from '../../modules/database/connections/oceano'; | ||||
| import { OCEANO_DATASOURCE } from '../../modules/database/connections/oceano/database.constants'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class AnagraficaService { | ||||
|   private readonly logger = new Logger(AnagraficaService.name); | ||||
| 
 | ||||
|   static readonly findPaginateConfig = (): PaginateConfig<SocioEntity> => ({ | ||||
|     relations: {}, | ||||
|     sortableColumns: ['codiceSocio', 'idNucleo', 'idPersona'], | ||||
|     searchableColumns: [], | ||||
|     defaultSortBy: [['codiceSocio', 'ASC']], | ||||
|     defaultLimit: 50, | ||||
|     maxLimit: -1, | ||||
|     filterableColumns: {}, | ||||
|     relativePath: true, | ||||
|     where: { | ||||
|       codiceGruppo: '6', | ||||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   constructor( | ||||
|     @InjectRepository(SocioEntity, OCEANO_DATASOURCE) private readonly sociRepository: Repository<SocioEntity> | ||||
|   ) {} | ||||
| 
 | ||||
|   async getAnagrafiche(query: PaginateQuery) { | ||||
|     const config = AnagraficaService.findPaginateConfig(); | ||||
|     query.limit ??= -1; | ||||
| 
 | ||||
|     return await paginate(query, this.sociRepository, config); | ||||
|   } | ||||
| 
 | ||||
|   async getOneAnagraficaById(idAnagrafica: string) { | ||||
|     return await this.sociRepository.findOne({ | ||||
|       where: { ...AnagraficaService.findPaginateConfig().where, codiceSocio: idAnagrafica }, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @ -1,19 +1,61 @@ | ||||
| /** | ||||
|  * This is not a production server yet! | ||||
|  * This is only a minimal backend to get started. | ||||
|  */ | ||||
| 
 | ||||
| import { Logger } from '@nestjs/common'; | ||||
| import { Logger, RequestMethod, VersioningType } from '@nestjs/common'; | ||||
| import { NestFactory } from '@nestjs/core'; | ||||
| import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; | ||||
| import { Logger as PinoLogger } from 'nestjs-pino'; | ||||
| 
 | ||||
| import { DocumentBuilder, SwaggerDocumentOptions, SwaggerModule } from '@nestjs/swagger'; | ||||
| import { flow } from 'lodash'; | ||||
| import { AppModule } from './app/app.module'; | ||||
| import { patchPublicDecoratorSupport } from './modules/auth/strategies/jwt/jwt-auth.guard'; | ||||
| import { LocalConfig, localConfig } from './modules/config/local.config'; | ||||
| import { patchTypeOrm } from './modules/database/utils/typeorm-patch'; | ||||
| import { patchNestjsSwagger } from './modules/validation/utils/nestjs-swagger-patches'; | ||||
| 
 | ||||
| async function bootstrap() { | ||||
|   const app = await NestFactory.create(AppModule); | ||||
|   const globalPrefix = 'api'; | ||||
|   app.setGlobalPrefix(globalPrefix); | ||||
|   const port = process.env.PORT || 3000; | ||||
|   await patchTypeOrm(); | ||||
|   await patchNestjsSwagger(); | ||||
| 
 | ||||
|   const appOpts = (() => { | ||||
|     const loggerOpts = { bufferLogs: true }; | ||||
|     return { | ||||
|       forceCloseConnections: true, | ||||
|       ...loggerOpts, | ||||
|     }; | ||||
|   })(); | ||||
|   const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter(appOpts)); | ||||
|   app.enableVersioning({ | ||||
|     type: VersioningType.URI, | ||||
|     defaultVersion: '1' | ||||
|   }); | ||||
|   app.enableShutdownHooks(); | ||||
| 
 | ||||
|   const config = app.get<LocalConfig>(localConfig.KEY); | ||||
|   const port = config.port || 3000; | ||||
| 
 | ||||
|   app.useLogger(app.get(PinoLogger)); | ||||
| 
 | ||||
|   const swaggerConfig = new DocumentBuilder().addBearerAuth().build(); | ||||
|   const swaggerOptions = { | ||||
|     operationIdFactory: (controllerKey: string, methodKey: string) => { | ||||
|       return `${controllerKey}_${methodKey}`; | ||||
|     }, | ||||
|   } satisfies SwaggerDocumentOptions; | ||||
|   const document = flow( | ||||
|     () => SwaggerModule.createDocument(app, swaggerConfig, swaggerOptions), | ||||
|     patchPublicDecoratorSupport | ||||
|   )(); | ||||
| 
 | ||||
|   SwaggerModule.setup(`docs`, app, document, { | ||||
|     swaggerOptions: { | ||||
|       operationsSorter: 'alpha', | ||||
|       displayOperationId: true, | ||||
|       filter: true, | ||||
|       persistAuthorization: true, | ||||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   await app.listen(port); | ||||
|   Logger.log(`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`); | ||||
|   Logger.log(`🚀 Application is running on: http://localhost:${port}`); | ||||
| } | ||||
| 
 | ||||
| bootstrap(); | ||||
| void bootstrap(); | ||||
|  | ||||
							
								
								
									
										31
									
								
								apps/ebitemp-api/src/modules/auth/auth.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								apps/ebitemp-api/src/modules/auth/auth.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| import { registerAs } from '@nestjs/config'; | ||||
| import { z } from 'zod'; | ||||
| import coerceRecordTypes from '../config/utils/coerce-record-types'; | ||||
| 
 | ||||
| export const authSchema = z.object({ | ||||
|   accessToken: z.object({ | ||||
|     secret: z.string(), | ||||
|     expTimeInSecs: z.number().finite().nonnegative(), | ||||
|   }), | ||||
|   refreshToken: z.object({ | ||||
|     secret: z.string(), | ||||
|     expTimeInSecs: z.number().finite().nonnegative(), | ||||
|   }), | ||||
| }); | ||||
| export type AuthConfig = z.infer<typeof authSchema>; | ||||
| 
 | ||||
| export const authConfig = registerAs('auth', () => { | ||||
|   const env = coerceRecordTypes(process.env); | ||||
| 
 | ||||
|   const config: AuthConfig = authSchema.strict().parse({ | ||||
|     accessToken: { | ||||
|       secret: env['JWT_ACCESS_TOKEN_SECRET'], | ||||
|       expTimeInSecs: env['JWT_ACCESS_TOKEN_EXPIRATION_TIME_IN_SECONDS'], | ||||
|     }, | ||||
|     refreshToken: { | ||||
|       secret: env['JWT_REFRESH_TOKEN_SECRET'], | ||||
|       expTimeInSecs: env['JWT_REFRESH_TOKEN_EXPIRATION_TIME_IN_SECONDS'], | ||||
|     } | ||||
|   }); | ||||
|   return config; | ||||
| }); | ||||
							
								
								
									
										31
									
								
								apps/ebitemp-api/src/modules/auth/auth.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								apps/ebitemp-api/src/modules/auth/auth.controller.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| import { Body, Controller, HttpCode, HttpStatus, Post, UseGuards } from '@nestjs/common'; | ||||
| import { ApiBearerAuth } from '@nestjs/swagger'; | ||||
| import { AccountsEntity } from '../database/connections/ebitemp-api/entities'; | ||||
| import { LoginDto, LoginResDto } from './auth.dto'; | ||||
| import { AuthService } from './auth.service'; | ||||
| import { AuthenticatedUser } from './authenticated-user.decorator'; | ||||
| import { Public } from './strategies/jwt/jwt-auth.guard'; | ||||
| import { JwtRefreshGuard } from './strategies/jwt/jwt-refresh.guard'; | ||||
| 
 | ||||
| @ApiBearerAuth() | ||||
| @Controller('auth') | ||||
| export class AuthController { | ||||
|   constructor(private readonly authService: AuthService) {} | ||||
| 
 | ||||
|   @HttpCode(HttpStatus.OK) | ||||
|   @Public() | ||||
|   @Post('login') | ||||
|   async logIn(@Body() body: LoginDto): Promise<LoginResDto> { | ||||
|     const { username, password } = body; | ||||
|     const user = await this.authService.getAuthenticatedUser(username, password); | ||||
|     return await this.authService.signJwts(user); | ||||
|   } | ||||
| 
 | ||||
|   @HttpCode(HttpStatus.OK) | ||||
|   @UseGuards(JwtRefreshGuard) | ||||
|   @Public(true) | ||||
|   @Post('refresh') | ||||
|   async refresh(@AuthenticatedUser() user: AccountsEntity): Promise<LoginResDto> { | ||||
|     return await this.authService.signJwts(user); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										19
									
								
								apps/ebitemp-api/src/modules/auth/auth.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								apps/ebitemp-api/src/modules/auth/auth.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| import { createZodDto } from '@anatine/zod-nestjs'; | ||||
| import { z } from 'zod'; | ||||
| import { AccountsEntitySchema } from '../database/connections/ebitemp-api/entities'; | ||||
| 
 | ||||
| export const loginSchema = z.object({ | ||||
|   username: AccountsEntitySchema.shape.username, | ||||
|   password: z.string().nonempty(), | ||||
| }); | ||||
| export type Login = z.infer<typeof loginSchema>; | ||||
| export class LoginDto extends createZodDto(loginSchema) {} | ||||
| 
 | ||||
| export const loginResSchema = z.object({ | ||||
|   accessToken: z.string().jwt(), | ||||
|   accessTokenExp: z.string().datetime(), | ||||
|   refreshToken: z.string().jwt(), | ||||
|   refreshTokenExp: z.string().datetime() | ||||
| }) | ||||
| export type LoginRes = z.infer<typeof loginResSchema>; | ||||
| export class LoginResDto extends createZodDto(loginResSchema) {} | ||||
							
								
								
									
										36
									
								
								apps/ebitemp-api/src/modules/auth/auth.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								apps/ebitemp-api/src/modules/auth/auth.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { APP_GUARD, Reflector } from '@nestjs/core'; | ||||
| import { PassportModule } from '@nestjs/passport'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { AccountsEntity } from '../database/connections/ebitemp-api/entities'; | ||||
| import { AuthController } from './auth.controller'; | ||||
| import { AuthService } from './auth.service'; | ||||
| import { JwtAccessTokenAuthStrategy } from './strategies/jwt/jwt-access-token-auth.strategy'; | ||||
| import { JwtAccessTokenModule } from './strategies/jwt/jwt-access-token.module'; | ||||
| import { JwtAuthGuard } from './strategies/jwt/jwt-auth.guard'; | ||||
| import { JwtRefreshTokenAuthStrategy } from './strategies/jwt/jwt-refresh-token-auth.strategy'; | ||||
| import { JwtRefreshTokenModule } from './strategies/jwt/jwt-refresh-token.module'; | ||||
| import { UsersAuthModule } from './users/users.module'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../database/connections/ebitemp-api/database.constants'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     TypeOrmModule.forFeature([AccountsEntity], EBITEMP_API_DATASOURCE), | ||||
|     JwtAccessTokenModule, | ||||
|     JwtRefreshTokenModule, | ||||
|     PassportModule, | ||||
|     UsersAuthModule, | ||||
|   ], | ||||
|   controllers: [AuthController], | ||||
|   providers: [ | ||||
|     { | ||||
|       provide: APP_GUARD, | ||||
|       useFactory: (reflector) => new JwtAuthGuard(reflector), | ||||
|       inject: [Reflector], | ||||
|     }, | ||||
|     AuthService, | ||||
|     JwtAccessTokenAuthStrategy, | ||||
|     JwtRefreshTokenAuthStrategy, | ||||
|   ], | ||||
| }) | ||||
| export class AppAuthModule {} | ||||
							
								
								
									
										67
									
								
								apps/ebitemp-api/src/modules/auth/auth.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								apps/ebitemp-api/src/modules/auth/auth.service.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common'; | ||||
| import { JwtService, JwtSignOptions } from '@nestjs/jwt'; | ||||
| import * as bcrypt from 'bcrypt'; | ||||
| import dayjs from 'dayjs'; | ||||
| import { isEmpty } from 'lodash'; | ||||
| import { AccountsEntity } from '../database/connections/ebitemp-api/entities'; | ||||
| import { TokenPayload } from './constants/token-payload.interface'; | ||||
| import { ACCESS_TOKEN_JWT_SERVICE } from './strategies/jwt/jwt-access-token.module'; | ||||
| import { REFRESH_TOKEN_JWT_SERVICE } from './strategies/jwt/jwt-refresh-token.module'; | ||||
| import { UsersAuthService } from './users/users-auth.service'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class AuthService { | ||||
|   private readonly logger = new Logger(AuthService.name); | ||||
| 
 | ||||
|   constructor( | ||||
|     private readonly usersAuthService: UsersAuthService, | ||||
|     @Inject(ACCESS_TOKEN_JWT_SERVICE) private readonly accessTokenJwtService: JwtService, | ||||
|     @Inject(REFRESH_TOKEN_JWT_SERVICE) private readonly refreshTokenJwtService: JwtService | ||||
|   ) {} | ||||
| 
 | ||||
|   public async signJwts(account: AccountsEntity) { | ||||
|     const { token: accessToken, exp: accessTokenExp } = await this.getAccessToken(account); | ||||
|     const { token: refreshToken, exp: refreshTokenExp } = await this.getRefreshToken(account); | ||||
| 
 | ||||
|     return { | ||||
|       accessToken: accessToken, | ||||
|       accessTokenExp: dayjs.unix(accessTokenExp).toJSON(), | ||||
|       refreshToken: refreshToken, | ||||
|       refreshTokenExp: dayjs.unix(refreshTokenExp).toJSON(), | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   public async getAccessToken(account: AccountsEntity, options?: JwtSignOptions) { | ||||
|     const payload: TokenPayload = { username: account.username, sub: account.id }; | ||||
|     const token = await this.accessTokenJwtService.signAsync(payload, options); | ||||
|     const exp = this.accessTokenJwtService.decode(token).exp; | ||||
|     return { token, exp }; | ||||
|   } | ||||
| 
 | ||||
|   public async getRefreshToken(account: AccountsEntity, options?: JwtSignOptions) { | ||||
|     const payload: TokenPayload = { username: account.username, sub: account.id }; | ||||
|     const token = await this.refreshTokenJwtService.signAsync(payload, options); | ||||
|     await this.usersAuthService.setCurrentRefreshTokenHash(account.id, token); | ||||
|     const exp = this.refreshTokenJwtService.decode(token).exp; | ||||
|     return { token, exp }; | ||||
|   } | ||||
| 
 | ||||
|   public async getAuthenticatedUser(username: string, password: string): Promise<AccountsEntity> { | ||||
|     try { | ||||
|       const account = await this.usersAuthService.getUserByUsername(username); | ||||
|       if (!account) throw new UnauthorizedException(`Username not found`); | ||||
|       await this.verifyPassword(password, account.password); | ||||
|       return account; | ||||
|     } catch (error) { | ||||
|       throw new UnauthorizedException(`Wrong credentials`); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async verifyPassword(plainTextPassword: string, hashedPassword: string | null) { | ||||
|     const isPasswordMatching = | ||||
|       hashedPassword && !isEmpty(hashedPassword) ? await bcrypt.compare(plainTextPassword, hashedPassword) : null; | ||||
|     if (!isPasswordMatching) { | ||||
|       throw new UnauthorizedException(`Wrong password`); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| import { createParamDecorator, ExecutionContext } from '@nestjs/common'; | ||||
| import { RequestWithUser } from './constants/request-with-user'; | ||||
| 
 | ||||
| export const AuthenticatedUser = createParamDecorator((data: unknown, ctx: ExecutionContext) => { | ||||
|   const request = ctx.switchToHttp().getRequest<RequestWithUser>(); | ||||
| 
 | ||||
|   return request.user; | ||||
| }); | ||||
| @ -0,0 +1,6 @@ | ||||
| import { FastifyRequest } from "fastify"; | ||||
| import { AccountsEntity } from "../../database/connections/ebitemp-api/entities"; | ||||
| 
 | ||||
| export interface RequestWithUser extends FastifyRequest { | ||||
|   user: AccountsEntity; | ||||
| } | ||||
| @ -0,0 +1,6 @@ | ||||
| export interface TokenPayload { | ||||
|   username: string; | ||||
|   sub: number; | ||||
|   iat?: number; | ||||
|   exp?: number; | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| import { Inject, Injectable, UnauthorizedException } from '@nestjs/common'; | ||||
| import { PassportStrategy } from '@nestjs/passport'; | ||||
| import { FastifyRequest } from 'fastify'; | ||||
| import { ClsService } from 'nestjs-cls'; | ||||
| import { ExtractJwt, Strategy } from 'passport-jwt'; | ||||
| import { AppClsStore } from '../../../cls/cls.interface'; | ||||
| import { AuthConfig, authConfig } from '../../auth.config'; | ||||
| import { TokenPayload } from '../../constants/token-payload.interface'; | ||||
| import { UsersAuthService } from '../../users/users-auth.service'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class JwtAccessTokenAuthStrategy extends PassportStrategy(Strategy, 'jwt-access-token') { | ||||
|   constructor( | ||||
|     @Inject(authConfig.KEY) authConfig: AuthConfig, | ||||
|     private readonly cls: ClsService<AppClsStore>, | ||||
|     private readonly userAuthService: UsersAuthService | ||||
|   ) { | ||||
|     super({ | ||||
|       jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | ||||
|       secretOrKey: authConfig.accessToken.secret, | ||||
|       passReqToCallback: true, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async validate(request: FastifyRequest, payload: TokenPayload) { | ||||
|     const account = await this.userAuthService.getUserById(payload.sub); | ||||
|     if (!account) throw new UnauthorizedException('Access Token Guard'); | ||||
|     this.cls.set('account', account); | ||||
|     return account; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { JwtService, JwtModule as NestJwtModule } from '@nestjs/jwt'; | ||||
| import { authConfig, AuthConfig } from '../../auth.config'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| 
 | ||||
| export const ACCESS_TOKEN_JWT_SERVICE = Symbol('ACCESS_TOKEN_JWT_SERVICE'); | ||||
| const accessTokenJwtProvider = { | ||||
|   provide: ACCESS_TOKEN_JWT_SERVICE, | ||||
|   useFactory: (jwtService: JwtService) => jwtService, | ||||
|   inject: [JwtService], | ||||
| }; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     ConfigModule.forFeature(authConfig), | ||||
|     NestJwtModule.registerAsync({ | ||||
|       imports: [...authConfig.asProvider().imports], | ||||
|       useFactory: async (authConfig: AuthConfig) => ({ | ||||
|         secret: authConfig.accessToken.secret, | ||||
|         signOptions: { | ||||
|           expiresIn: `${authConfig.accessToken.expTimeInSecs}s`, | ||||
|         }, | ||||
|       }), | ||||
|       inject: [authConfig.KEY], | ||||
|     }), | ||||
|   ], | ||||
|   providers: [accessTokenJwtProvider], | ||||
|   exports: [ConfigModule, accessTokenJwtProvider], | ||||
| }) | ||||
| export class JwtAccessTokenModule {} | ||||
| @ -0,0 +1,39 @@ | ||||
| import { applyDecorators, ExecutionContext, SetMetadata } from '@nestjs/common'; | ||||
| import { Reflector } from '@nestjs/core'; | ||||
| import { AuthGuard } from '@nestjs/passport'; | ||||
| import { OpenAPIObject } from '@nestjs/swagger'; | ||||
| 
 | ||||
| export const IS_PUBLIC_KEY = 'isPublic'; | ||||
| export const Public = (keepSecurity: boolean = false) => { | ||||
|   const decorators = [SetMetadata(IS_PUBLIC_KEY, true)]; | ||||
|   if (!keepSecurity) decorators.push(SetMetadata('swagger/apiSecurity', ['public'])); | ||||
|   return applyDecorators(...decorators); | ||||
| }; | ||||
| 
 | ||||
| export const patchPublicDecoratorSupport = (document: OpenAPIObject) => { | ||||
|   Object.values(document.paths).forEach((path: any) => { | ||||
|     Object.values(path).forEach((method: any) => { | ||||
|       if (Array.isArray(method.security) && method.security.includes('public')) { | ||||
|         method.security = []; | ||||
|       } | ||||
|     }); | ||||
|   }); | ||||
|   return document; | ||||
| }; | ||||
| 
 | ||||
| export class JwtAuthGuard extends AuthGuard('jwt-access-token') { | ||||
|   constructor(private reflector: Reflector) { | ||||
|     super(); | ||||
|   } | ||||
| 
 | ||||
|   canActivate(context: ExecutionContext) { | ||||
|     const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [ | ||||
|       context.getHandler(), | ||||
|       context.getClass(), | ||||
|     ]); | ||||
|     if (isPublic) { | ||||
|       return true; | ||||
|     } | ||||
|     return super.canActivate(context); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,32 @@ | ||||
| import { Inject, Injectable, UnauthorizedException } from '@nestjs/common'; | ||||
| import { PassportStrategy } from '@nestjs/passport'; | ||||
| import { FastifyRequest } from 'fastify'; | ||||
| import { ClsService } from 'nestjs-cls'; | ||||
| import { ExtractJwt, Strategy } from 'passport-jwt'; | ||||
| import { AppClsStore } from '../../../cls/cls.interface'; | ||||
| import { authConfig, AuthConfig } from '../../auth.config'; | ||||
| import { TokenPayload } from '../../constants/token-payload.interface'; | ||||
| import { UsersAuthService } from '../../users/users-auth.service'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class JwtRefreshTokenAuthStrategy extends PassportStrategy(Strategy, 'jwt-refresh-token') { | ||||
|   constructor( | ||||
|     @Inject(authConfig.KEY) authConfig: AuthConfig, | ||||
|     private readonly cls: ClsService<AppClsStore>, | ||||
|     private readonly usersAuthService: UsersAuthService | ||||
|   ) { | ||||
|     super({ | ||||
|       jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | ||||
|       secretOrKey: authConfig.refreshToken.secret, | ||||
|       passReqToCallback: true, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async validate(request: FastifyRequest, payload: TokenPayload) { | ||||
|     const refreshToken = (request.headers?.authorization as string | undefined)?.replace('Bearer', '')?.trim() ?? ''; | ||||
|     const account = await this.usersAuthService.getUserByIdAndRefreshTokenPair(payload.sub, refreshToken); | ||||
|     if (!account) throw new UnauthorizedException('Refresh Token Guard'); | ||||
|     this.cls.set('account', account); | ||||
|     return account; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| import { JwtService, JwtModule as NestJwtModule } from '@nestjs/jwt'; | ||||
| import { authConfig, AuthConfig } from '../../auth.config'; | ||||
| 
 | ||||
| export const REFRESH_TOKEN_JWT_SERVICE = Symbol('REFRESH_TOKEN_JWT_SERVICE'); | ||||
| const refreshTokenJwtProvider = { | ||||
|   provide: REFRESH_TOKEN_JWT_SERVICE, | ||||
|   useFactory: (jwtService: JwtService) => jwtService, | ||||
|   inject: [JwtService], | ||||
| }; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     ConfigModule.forFeature(authConfig), | ||||
|     NestJwtModule.registerAsync({ | ||||
|       imports: [...authConfig.asProvider().imports], | ||||
|       useFactory: async (authConfig: AuthConfig) => ({ | ||||
|         secret: authConfig.refreshToken.secret, | ||||
|         signOptions: { | ||||
|           expiresIn: `${authConfig.refreshToken.expTimeInSecs}s`, | ||||
|         }, | ||||
|       }), | ||||
|       inject: [authConfig.KEY], | ||||
|     }), | ||||
|   ], | ||||
|   providers: [refreshTokenJwtProvider], | ||||
|   exports: [ConfigModule, refreshTokenJwtProvider], | ||||
| }) | ||||
| export class JwtRefreshTokenModule {} | ||||
| @ -0,0 +1,3 @@ | ||||
| import { AuthGuard } from '@nestjs/passport'; | ||||
| 
 | ||||
| export class JwtRefreshGuard extends AuthGuard('jwt-refresh-token') {} | ||||
| @ -0,0 +1,43 @@ | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { InjectRepository } from '@nestjs/typeorm'; | ||||
| import * as argon2 from 'argon2'; | ||||
| import { Repository } from 'typeorm'; | ||||
| import { AccountsEntity } from '../../database/connections/ebitemp-api/entities'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../../database/connections/ebitemp-api/database.constants'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class UsersAuthService { | ||||
|   constructor( | ||||
|     @InjectRepository(AccountsEntity, EBITEMP_API_DATASOURCE) | ||||
|     private readonly accountsRepository: Repository<AccountsEntity> | ||||
|   ) {} | ||||
| 
 | ||||
|   async getUserById(accountId: number) { | ||||
|     return await this.accountsRepository.findOne({ | ||||
|       relations: { profili: { ruolo: true } }, | ||||
|       where: { id: accountId }, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async getUserByUsername(username: string) { | ||||
|     return await this.accountsRepository.findOne({ | ||||
|       relations: { profili: { ruolo: true } }, | ||||
|       where: { username: username }, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async getUserByIdAndRefreshTokenPair(accountId: number, refreshToken: string) { | ||||
|     const accountById = await this.getUserById(accountId); | ||||
|     if (!accountById?.ultimoHashRefreshToken) return null; | ||||
| 
 | ||||
|     const isRefreshTokenMatching = await argon2.verify(accountById.ultimoHashRefreshToken, refreshToken); | ||||
|     return isRefreshTokenMatching ? accountById : null; | ||||
|   } | ||||
| 
 | ||||
|   async setCurrentRefreshTokenHash(accountId: number, refreshToken: string | null) { | ||||
|     const hash = refreshToken ? await argon2.hash(refreshToken) : null; | ||||
|     await this.accountsRepository.update(accountId, { | ||||
|       ultimoHashRefreshToken: hash, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										14
									
								
								apps/ebitemp-api/src/modules/auth/users/users.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								apps/ebitemp-api/src/modules/auth/users/users.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| 
 | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { UsersAuthService } from './users-auth.service'; | ||||
| import { AccountsEntity } from '../../database/connections/ebitemp-api/entities'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../../database/connections/ebitemp-api/database.constants'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [TypeOrmModule.forFeature([AccountsEntity], EBITEMP_API_DATASOURCE)], | ||||
|   controllers: [], | ||||
|   providers: [UsersAuthService], | ||||
|   exports: [UsersAuthService], | ||||
| }) | ||||
| export class UsersAuthModule {} | ||||
							
								
								
									
										6
									
								
								apps/ebitemp-api/src/modules/cls/cls.interface.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								apps/ebitemp-api/src/modules/cls/cls.interface.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| import { ClsStore } from "nestjs-cls"; | ||||
| import { AccountsEntity } from "../database/connections/ebitemp-api"; | ||||
| 
 | ||||
| export interface AppClsStore extends ClsStore { | ||||
|   account: AccountsEntity|null; | ||||
| } | ||||
							
								
								
									
										16
									
								
								apps/ebitemp-api/src/modules/cls/cls.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								apps/ebitemp-api/src/modules/cls/cls.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { ClsModule } from 'nestjs-cls'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     ClsModule.forRoot({ | ||||
|       global: true, | ||||
|       middleware: { | ||||
|         mount: true, | ||||
|       }, | ||||
|     }), | ||||
|   ], | ||||
|   providers: [], | ||||
|   exports: [ClsModule], | ||||
| }) | ||||
| export class AppClsModule {} | ||||
							
								
								
									
										13
									
								
								apps/ebitemp-api/src/modules/config/config.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								apps/ebitemp-api/src/modules/config/config.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| import { localConfig } from './local.config'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     ConfigModule.forRoot({ | ||||
|       isGlobal: true, | ||||
|       load: [localConfig], | ||||
|     }), | ||||
|   ], | ||||
| }) | ||||
| export class AppConfigModule {} | ||||
							
								
								
									
										19
									
								
								apps/ebitemp-api/src/modules/config/local.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								apps/ebitemp-api/src/modules/config/local.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| import coerceRecordTypes from './utils/coerce-record-types'; | ||||
| import { registerAs } from '@nestjs/config'; | ||||
| import { z } from 'zod'; | ||||
| 
 | ||||
| export const localSchema = z.object({ | ||||
|   production: z.boolean(), | ||||
|   port: z.number().optional(), | ||||
| }); | ||||
| export type LocalConfig = z.infer<typeof localSchema>; | ||||
| 
 | ||||
| export const localConfig = registerAs('local', () => { | ||||
|   const env = coerceRecordTypes(process.env); | ||||
| 
 | ||||
|   const config: LocalConfig = localSchema.strict().parse({ | ||||
|     production: env['PRODUCTION'], | ||||
|     port: env['PORT'], | ||||
|   }); | ||||
|   return config; | ||||
| }); | ||||
| @ -0,0 +1,11 @@ | ||||
| import { clone, each } from 'lodash'; | ||||
| 
 | ||||
| export default (raw: Record<string, any>): Record<string, string | number | boolean | undefined> => { | ||||
|   raw = clone(raw); | ||||
|   return each(raw, (value, key) => { | ||||
|     if (value === 'true') raw[key] = true; | ||||
|     if (value === 'false') raw[key] = false; | ||||
|     if (value === 'null') raw[key] = null; | ||||
|     if (!isNaN(Number(value))) raw[key] = Number(value); | ||||
|   }); | ||||
| }; | ||||
| @ -0,0 +1,10 @@ | ||||
| import coerceRecordTypes from '../../../config/utils/coerce-record-types'; | ||||
| import { databaseConfigFactory, rawDatabaseSchema } from '../../utils/database-config'; | ||||
| 
 | ||||
| const env = coerceRecordTypes(process.env); | ||||
| const envParsed = rawDatabaseSchema.strict().parse({ | ||||
|   connectionString: env['DATABASE_EBITEMPAPI_CONNECTION_STRING'], | ||||
|   secure: env['DATABASE_EBITEMPAPI_SECURE'], | ||||
| }); | ||||
| 
 | ||||
| export const databaseConfig = databaseConfigFactory(envParsed); | ||||
| @ -0,0 +1 @@ | ||||
| export const EBITEMP_API_DATASOURCE = "EbitempApi"; | ||||
| @ -0,0 +1,32 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { DatabaseConfig } from '../../utils/database-config'; | ||||
| import { typeormTransactionalDataSourceFactory } from '../../utils/typeorm-data-source-factory'; | ||||
| import { typeormEntitiesFromImport } from '../../utils/typeorm-import-entities'; | ||||
| import { typeormModuleOptionsFactory } from '../../utils/typeorm-module-options-factory'; | ||||
| import { databaseConfig } from './database.config'; | ||||
| import { EBITEMP_API_DATASOURCE } from './database.constants'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     ConfigModule.forFeature(databaseConfig), | ||||
|     TypeOrmModule.forRootAsync({ | ||||
|       imports: databaseConfig.asProvider().imports, | ||||
|       name: EBITEMP_API_DATASOURCE, | ||||
|       dataSourceFactory: typeormTransactionalDataSourceFactory(EBITEMP_API_DATASOURCE), | ||||
|       useFactory: async (dbConfig: DatabaseConfig) => { | ||||
|         const config = await typeormModuleOptionsFactory( | ||||
|           dbConfig, | ||||
|           await typeormEntitiesFromImport(await import('./index')), | ||||
|           EBITEMP_API_DATASOURCE | ||||
|         ); | ||||
|         return config; | ||||
|       }, | ||||
|       inject: [databaseConfig.KEY], | ||||
|     }), | ||||
|   ], | ||||
|   providers: [], | ||||
|   exports: [TypeOrmModule], | ||||
| }) | ||||
| export class EbitempApiDatabaseModule {} | ||||
| @ -0,0 +1,51 @@ | ||||
| import { toZod } from 'tozod'; | ||||
| import { Column, Entity, Index, OneToMany } from 'typeorm'; | ||||
| import { z } from 'zod'; | ||||
| import { ProfiliEntity, ProfiliEntitySchema } from './profili.entity'; | ||||
| 
 | ||||
| @Index('pk_accounts', ['id'], { unique: true }) | ||||
| @Entity('accounts') | ||||
| export class AccountsEntity { | ||||
|   @Column('int', { primary: true, name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'username', unique: true, length: 255 }) | ||||
|   username!: string; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'password', nullable: true, length: 255 }) | ||||
|   password!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'nome', length: 255 }) | ||||
|   nome!: string; | ||||
| 
 | ||||
|   @Column('date', { name: 'data_creazione' }) | ||||
|   dataCreazione!: Date; | ||||
| 
 | ||||
|   @Column('date', { name: 'data_scadenza', nullable: true }) | ||||
|   dataScadenza!: Date | null; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'ultimo_hash_refresh_token', | ||||
|     nullable: true, | ||||
|     length: 1024, | ||||
|   }) | ||||
|   ultimoHashRefreshToken!: string | null; | ||||
| 
 | ||||
|   @OneToMany(() => ProfiliEntity, (profiliEntity) => profiliEntity.account) | ||||
|   profili!: ProfiliEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<AccountsEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const AccountsEntitySchema: toZod<AccountsEntity> = z.late.object(() => ({ | ||||
|   id: z.number().finite(), | ||||
|   username: z.string().nonempty(), | ||||
|   password: z.string().nonempty().nullable(), | ||||
|   nome: z.string().nonempty(), | ||||
|   dataCreazione: z.date(), | ||||
|   dataScadenza: z.date().nullable(), | ||||
|   ultimoHashRefreshToken: z.string().nonempty().nullable(), | ||||
|   profili: z.array(ProfiliEntitySchema), | ||||
| })); | ||||
| @ -0,0 +1,25 @@ | ||||
| import { Column, Entity, Index, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; | ||||
| import { ApiClientInvocazioniEntity } from './api_client_invocazioni.entity'; | ||||
| 
 | ||||
| @Index('pk_api_client', ['id'], { unique: true }) | ||||
| @Entity('api_client') | ||||
| export class ApiClientEntity { | ||||
|   @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'nome', nullable: true, length: 255 }) | ||||
|   nome!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'descrizione', nullable: true, length: 255 }) | ||||
|   descrizione!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'base_url', length: 2000 }) | ||||
|   baseUrl!: string; | ||||
| 
 | ||||
|   @OneToMany(() => ApiClientInvocazioniEntity, (apiClientInvocazioniEntity) => apiClientInvocazioniEntity.client) | ||||
|   invocazioni!: ApiClientInvocazioniEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<ApiClientEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; | ||||
| import { ApiClientEntity } from './api_client.entity'; | ||||
| 
 | ||||
| @Index('pk_api_client_invocazioni', ['id'], { unique: true }) | ||||
| @Entity('api_client_invocazioni') | ||||
| export class ApiClientInvocazioniEntity { | ||||
|   @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'id_client' }) | ||||
|   idClient!: number; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'percorso', length: 2000 }) | ||||
|   percorso!: string; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'metodo', length: 7 }) | ||||
|   metodo!: string; | ||||
| 
 | ||||
|   @Column('datetime', { name: 'timestamp' }) | ||||
|   timestamp!: Date; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'richiesta', nullable: true }) | ||||
|   richiesta!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'risposta' }) | ||||
|   risposta!: string; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'errori', nullable: true }) | ||||
|   errori!: string | null; | ||||
| 
 | ||||
|   @Column('bit', { name: 'flag_errore' }) | ||||
|   flagErrore!: boolean; | ||||
| 
 | ||||
|   @ManyToOne(() => ApiClientEntity, (apiClientEntity) => apiClientEntity.invocazioni) | ||||
|   @JoinColumn([{ name: 'id_client', referencedColumnName: 'id' }]) | ||||
|   client!: ApiClientEntity; | ||||
| 
 | ||||
|   constructor(init?: Partial<ApiClientInvocazioniEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| import { Column, Entity, Index, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; | ||||
| import { ApiEndpointInvocazioniEntity } from './api_endpoint_invocazioni.entity'; | ||||
| 
 | ||||
| @Index('pk_api_endpoint', ['id'], { unique: true }) | ||||
| @Entity('api_endpoint') | ||||
| export class ApiEndpointEntity { | ||||
|   @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'percorso', length: 2000 }) | ||||
|   percorso!: string; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'metodo', length: 7 }) | ||||
|   metodo!: string; | ||||
| 
 | ||||
|   @OneToMany( | ||||
|     () => ApiEndpointInvocazioniEntity, | ||||
|     (apiEndpointInvocazioniEntity) => apiEndpointInvocazioniEntity.endpoint | ||||
|   ) | ||||
|   invocazioni!: ApiEndpointInvocazioniEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<ApiEndpointEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,40 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; | ||||
| import { AccountsEntity } from './accounts.entity'; | ||||
| import { ApiEndpointEntity } from './api_endpoint.entity'; | ||||
| 
 | ||||
| @Index('pk_api_endpoint_invocazioni', ['id'], { unique: true }) | ||||
| @Entity('api_endpoint_invocazioni') | ||||
| export class ApiEndpointInvocazioniEntity { | ||||
|   @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'id_account' }) | ||||
|   idAccount!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'id_endpoint' }) | ||||
|   idEndpoint!: number; | ||||
| 
 | ||||
|   @Column('datetime', { name: 'timestamp' }) | ||||
|   timestamp!: Date; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'richiesta' }) | ||||
|   richiesta!: string; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'risposta' }) | ||||
|   risposta!: string; | ||||
| 
 | ||||
|   @Column('bit', { name: 'flag_errore' }) | ||||
|   flagErrore!: boolean; | ||||
| 
 | ||||
|   @ManyToOne(() => ApiEndpointEntity, (apiEndpointEntity) => apiEndpointEntity.invocazioni) | ||||
|   @JoinColumn([{ name: 'id_endpoint', referencedColumnName: 'id' }]) | ||||
|   endpoint!: ApiEndpointEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => AccountsEntity) | ||||
|   @JoinColumn([{ name: 'id_account', referencedColumnName: 'id' }]) | ||||
|   account!: AccountsEntity; | ||||
| 
 | ||||
|   constructor(init?: Partial<ApiEndpointInvocazioniEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| export * from './accounts.entity'; | ||||
| export * from './api_client.entity'; | ||||
| export * from './api_client_invocazioni.entity'; | ||||
| export * from './api_endpoint.entity'; | ||||
| export * from './api_endpoint_invocazioni.entity'; | ||||
| export * from './profili.entity'; | ||||
| export * from './ruoli.entity'; | ||||
| export * from './tipi_jobs.entity'; | ||||
| @ -0,0 +1,38 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm'; | ||||
| import { AccountsEntity, AccountsEntitySchema } from './accounts.entity'; | ||||
| import { RuoliEntity, RuoliEntitySchema } from './ruoli.entity'; | ||||
| import { toZod } from 'tozod'; | ||||
| import { z } from 'zod'; | ||||
| 
 | ||||
| @Index('pk_profili', ['id'], { unique: true }) | ||||
| @Entity('profili') | ||||
| export class ProfiliEntity { | ||||
|   @Column('int', { primary: true, name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'id_account', unique: true }) | ||||
|   idAccount!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'id_ruolo', unique: true }) | ||||
|   idRuolo!: number; | ||||
| 
 | ||||
|   @ManyToOne(() => AccountsEntity, (accountsEntity) => accountsEntity.profili) | ||||
|   @JoinColumn([{ name: 'id_account', referencedColumnName: 'id' }]) | ||||
|   account!: AccountsEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => RuoliEntity, (ruoliEntity) => ruoliEntity.profili) | ||||
|   @JoinColumn([{ name: 'id_ruolo', referencedColumnName: 'id' }]) | ||||
|   ruolo!: RuoliEntity; | ||||
| 
 | ||||
|   constructor(init?: Partial<ProfiliEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const ProfiliEntitySchema: toZod<ProfiliEntity> = z.late.object(() => ({ | ||||
|   id: z.number().finite(), | ||||
|   idAccount: AccountsEntitySchema.shape.id, | ||||
|   idRuolo: RuoliEntitySchema.shape.id, | ||||
|   account: AccountsEntitySchema, | ||||
|   ruolo: RuoliEntitySchema, | ||||
| })); | ||||
| @ -0,0 +1,31 @@ | ||||
| import { Column, Entity, Index, OneToMany } from 'typeorm'; | ||||
| import { ProfiliEntity, ProfiliEntitySchema } from './profili.entity'; | ||||
| import { toZod } from 'tozod'; | ||||
| import { z } from 'zod'; | ||||
| 
 | ||||
| @Index('pk_ruoli', ['id'], { unique: true }) | ||||
| @Entity('ruoli') | ||||
| export class RuoliEntity { | ||||
|   @Column('int', { primary: true, name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'nome', unique: true, length: 255 }) | ||||
|   nome!: string; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'descrizione', length: 255 }) | ||||
|   descrizione!: string; | ||||
| 
 | ||||
|   @OneToMany(() => ProfiliEntity, (profiliEntity) => profiliEntity.ruolo) | ||||
|   profili!: ProfiliEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<RuoliEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const RuoliEntitySchema: toZod<RuoliEntity> = z.late.object(() => ({ | ||||
|   id: z.number().finite(), | ||||
|   nome: z.string().nonempty(), | ||||
|   descrizione: z.string().nonempty(), | ||||
|   profili: z.array(ProfiliEntitySchema), | ||||
| })); | ||||
| @ -0,0 +1,49 @@ | ||||
| import { Column, Entity, Index } from 'typeorm'; | ||||
| import { Enum, Enumify } from '../../../../enumify/enumify'; | ||||
| 
 | ||||
| export class InviaMailSeErrori { | ||||
|   @Column('bit', { name: 'flag', default: () => '(0)' }) | ||||
|   flagAttivo!: boolean; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'oggetto', nullable: true, length: 255 }) | ||||
|   oggetto!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'destinatari', nullable: true, length: 255 }) | ||||
|   destinatari!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'cc', nullable: true, length: 255 }) | ||||
|   cc!: string | null; | ||||
| } | ||||
| 
 | ||||
| @Index('pk_tipi_jobs', ['id'], { unique: true }) | ||||
| @Entity('tipi_jobs') | ||||
| export class TipiJobsEntity extends Enumify { | ||||
|   static _ = TipiJobsEntity.closeEnum(); | ||||
| 
 | ||||
|   @Column('int', { primary: true, name: 'id' }) | ||||
|   id!: number; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'nome', length: 50 }) | ||||
|   nome!: string; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'descrizione', nullable: true, length: 255 }) | ||||
|   descrizione!: string | null; | ||||
| 
 | ||||
|   @Column(() => InviaMailSeErrori, { prefix: 'invia_mail_se_errori'}) | ||||
|   inviaMailSeErrori!: InviaMailSeErrori; | ||||
| 
 | ||||
|   @Column('bit', { name: 'flag_attivo', default: () => '(0)' }) | ||||
|   flagAttivo!: boolean; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'pattern_cron', length: 20 }) | ||||
|   patternCron!: string; | ||||
| 
 | ||||
|   public constructor( | ||||
|     id: number, | ||||
|     nome: string, | ||||
|     patternCron: string, | ||||
|   ) { | ||||
|     super(id, nome); | ||||
|     this.patternCron = patternCron; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| export * from './entities'; | ||||
| @ -0,0 +1,10 @@ | ||||
| import coerceRecordTypes from '../../../config/utils/coerce-record-types'; | ||||
| import { databaseConfigFactory, rawDatabaseSchema } from '../../utils/database-config'; | ||||
| 
 | ||||
| const env = coerceRecordTypes(process.env); | ||||
| const envParsed = rawDatabaseSchema.strict().parse({ | ||||
|   connectionString: env['DATABASE_OCEANO_CONNECTION_STRING'], | ||||
|   secure: env['DATABASE_OCEANO_SECURE'], | ||||
| }); | ||||
| 
 | ||||
| export const databaseConfig = databaseConfigFactory(envParsed); | ||||
| @ -0,0 +1 @@ | ||||
| export const OCEANO_DATASOURCE = "Oceano"; | ||||
| @ -0,0 +1,32 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { DatabaseConfig } from '../../utils/database-config'; | ||||
| import { typeormTransactionalDataSourceFactory } from '../../utils/typeorm-data-source-factory'; | ||||
| import { typeormEntitiesFromImport } from '../../utils/typeorm-import-entities'; | ||||
| import { typeormModuleOptionsFactory } from '../../utils/typeorm-module-options-factory'; | ||||
| import { databaseConfig } from './database.config'; | ||||
| import { OCEANO_DATASOURCE } from './database.constants'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     ConfigModule.forFeature(databaseConfig), | ||||
|     TypeOrmModule.forRootAsync({ | ||||
|       imports: databaseConfig.asProvider().imports, | ||||
|       name: OCEANO_DATASOURCE, | ||||
|       dataSourceFactory: typeormTransactionalDataSourceFactory(OCEANO_DATASOURCE), | ||||
|       useFactory: async (dbConfig: DatabaseConfig) => { | ||||
|         const config = await typeormModuleOptionsFactory( | ||||
|           dbConfig, | ||||
|           await typeormEntitiesFromImport(await import('./index')), | ||||
|           OCEANO_DATASOURCE | ||||
|         ); | ||||
|         return config; | ||||
|       }, | ||||
|       inject: [databaseConfig.KEY], | ||||
|     }), | ||||
|   ], | ||||
|   providers: [], | ||||
|   exports: [TypeOrmModule], | ||||
| }) | ||||
| export class OceanoDatabaseModule {} | ||||
| @ -0,0 +1,20 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm'; | ||||
| import { ApiLuoghiEntity } from './API_Luoghi.entity'; | ||||
| 
 | ||||
| @Index('nk_API_CAP_Luoghi', ['codice', 'cap'], { unique: true }) | ||||
| @Entity('API_CAP_Luoghi') | ||||
| export class ApiCapLuoghiEntity { | ||||
|   @Column('varchar', { primary: true, name: 'codice', length: 5 }) | ||||
|   codice!: string; | ||||
| 
 | ||||
|   @Column('varchar', { primary: true, name: 'cap', length: 5 }) | ||||
|   cap!: string; | ||||
| 
 | ||||
|   @ManyToOne(() => ApiLuoghiEntity) | ||||
|   @JoinColumn([{ name: 'codice', referencedColumnName: 'codice' }]) | ||||
|   luogo!: ApiLuoghiEntity; | ||||
| 
 | ||||
|   constructor(init?: Partial<ApiCapLuoghiEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,38 @@ | ||||
| import { Column, Entity, Index, OneToMany } from 'typeorm'; | ||||
| import { ApiCapLuoghiEntity } from './API_CAP_Luoghi.entity'; | ||||
| import { SocioEntity } from './socio.entity'; | ||||
| 
 | ||||
| @Index('PK_API_Luoghi', ['codice'], { unique: true }) | ||||
| @Entity('API_Luoghi') | ||||
| export class ApiLuoghiEntity { | ||||
|   @Column('varchar', { primary: true, name: 'codice', length: 5 }) | ||||
|   codice!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'comune', nullable: true, length: 255 }) | ||||
|   comune!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'sigla_provincia', nullable: true, length: 5 }) | ||||
|   siglaProvincia!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'provincia', nullable: true, length: 255 }) | ||||
|   provincia!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codice_regione', nullable: true, length: 5 }) | ||||
|   codiceRegione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'regione', nullable: true, length: 255 }) | ||||
|   regione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codice_stato', nullable: true, length: 5 }) | ||||
|   codiceStato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'stato', nullable: true, length: 255 }) | ||||
|   stato!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'flag_attivo', nullable: true }) | ||||
|   flagAttivo!: number | null; | ||||
| 
 | ||||
|   constructor(init?: Partial<ApiLuoghiEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| import { Column, Entity, PrimaryColumn } from 'typeorm'; | ||||
| 
 | ||||
| @Entity('API_Luoghi_Estero') | ||||
| export class ApiLuoghiEsteroEntity { | ||||
|   @PrimaryColumn('varchar', { name: 'codice', length: 5 }) | ||||
|   codice!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codice_stato', length: 5 }) | ||||
|   codiceStato!: string; | ||||
| 
 | ||||
|   constructor(init?: Partial<ApiLuoghiEsteroEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,84 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm'; | ||||
| 
 | ||||
| @Index('PK_allegati', ['codiceAllegato'], { unique: true }) | ||||
| @Entity('allegati') | ||||
| export class AllegatiEntity { | ||||
|   @Column('bigint', { primary: true, name: 'codiceAllegato' }) | ||||
|   codiceAllegato!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'tipoOrigine', nullable: true }) | ||||
|   tipoOrigine!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceOrigine', nullable: true }) | ||||
|   codiceOrigine!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'descrizioneAllegato', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   descrizioneAllegato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataAllegato', nullable: true, length: 10 }) | ||||
|   dataAllegato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'cartellaAllegato', | ||||
|     nullable: true, | ||||
|     length: 2048, | ||||
|   }) | ||||
|   cartellaAllegato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'nomeFileAllegato', nullable: true, length: 255 }) | ||||
|   nomeFileAllegato!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteAllegato', nullable: true }) | ||||
|   noteAllegato!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteCreazione', nullable: true }) | ||||
|   utenteCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataCreazione', nullable: true, length: 10 }) | ||||
|   dataCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraCreazione', nullable: true, length: 8 }) | ||||
|   oraCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteModifica', nullable: true }) | ||||
|   utenteModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataModifica', nullable: true, length: 10 }) | ||||
|   dataModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraModifica', nullable: true, length: 8 }) | ||||
|   oraModifica!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'pubblicatoWEB', nullable: true }) | ||||
|   pubblicatoWeb!: number | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'trasferitoWEB', | ||||
|     nullable: true, | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   trasferitoWeb!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataPubblicazione', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   dataPubblicazione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { | ||||
|     name: 'codiceProtocolloGenerico', | ||||
|     nullable: true, | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   codiceProtocolloGenerico!: string | null; | ||||
| 
 | ||||
|   constructor(init?: Partial<AllegatiEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,492 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; | ||||
| import { EnteEntity } from './ente.entity'; | ||||
| import { SocioContrattoEntity } from './socioContratto.entity'; | ||||
| 
 | ||||
| @Index('PK_contratto', ['codiceContratto'], { unique: true }) | ||||
| @Entity('contratto') | ||||
| export class ContrattoEntity { | ||||
|   @Column('bigint', { primary: true, name: 'codiceContratto' }) | ||||
|   codiceContratto!: string; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceEnte', nullable: true }) | ||||
|   codiceEnte!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'contratto', nullable: true, length: 255 }) | ||||
|   contratto!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataContratto', nullable: true, length: 10 }) | ||||
|   dataContratto!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'rinnovoAutomatico', nullable: true }) | ||||
|   rinnovoAutomatico!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataDecorrenzaContratto', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataDecorrenzaContratto!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'codiceEconomicoEsolver', | ||||
|     nullable: true, | ||||
|     length: 30, | ||||
|   }) | ||||
|   codiceEconomicoEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'codicePatrimonialeEsolver', | ||||
|     nullable: true, | ||||
|     length: 30, | ||||
|   }) | ||||
|   codicePatrimonialeEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'iscrizioneFamiliariObbligatoria', nullable: true }) | ||||
|   iscrizioneFamiliariObbligatoria!: number | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'inizioDecorrenzaNuoveAdesioniAderenti', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   inizioDecorrenzaNuoveAdesioniAderenti!: number | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'inizioDecorrenzaNuoveAdesioniFamiliari', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   inizioDecorrenzaNuoveAdesioniFamiliari!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'cessazioneInfraAnnuale', nullable: true }) | ||||
|   cessazioneInfraAnnuale!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'numeroGiorniRimborso', nullable: true }) | ||||
|   numeroGiorniRimborso!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'rimborsoPersona', nullable: true }) | ||||
|   rimborsoPersona!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'rimborsoFax', nullable: true }) | ||||
|   rimborsoFax!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'rimborsoEmail', nullable: true }) | ||||
|   rimborsoEmail!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'rimborsoPosta', nullable: true }) | ||||
|   rimborsoPosta!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'rimborsoWeb', nullable: true }) | ||||
|   rimborsoWeb!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utentecreazione', nullable: true }) | ||||
|   utentecreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataCreazione', nullable: true, length: 10 }) | ||||
|   dataCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraCreazione', nullable: true, length: 8 }) | ||||
|   oraCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteModifica', nullable: true }) | ||||
|   utenteModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataModifica', nullable: true, length: 10 }) | ||||
|   dataModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraModifica', nullable: true, length: 8 }) | ||||
|   oraModifica!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteContratto', nullable: true }) | ||||
|   noteContratto!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'versamentiTerzi', nullable: true }) | ||||
|   versamentiTerzi!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'ignoraControlloPagamenti', nullable: true }) | ||||
|   ignoraControlloPagamenti!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'anniRinnovo', nullable: true }) | ||||
|   anniRinnovo!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceRivenditore', nullable: true }) | ||||
|   codiceRivenditore!: string | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'inizioDecorrenzaNuoveAdesioniNascita', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   inizioDecorrenzaNuoveAdesioniNascita!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'numeroGiorniNascita', nullable: true }) | ||||
|   numeroGiorniNascita!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'mesiEsclusionePatologieAderenti', nullable: true }) | ||||
|   mesiEsclusionePatologieAderenti!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'mesiEsclusionePatologieFamiliari', nullable: true }) | ||||
|   mesiEsclusionePatologieFamiliari!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'coperturaPreesistenzeAderenti', nullable: true }) | ||||
|   coperturaPreesistenzeAderenti!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'coperturaPreesistenzeFamiliari', nullable: true }) | ||||
|   coperturaPreesistenzeFamiliari!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'adesioniFdaIndividuali', nullable: true }) | ||||
|   adesioniFdaIndividuali!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataAssunzioneAderenti', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataAssunzioneAderenti!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'associazioniIndividuali', nullable: true }) | ||||
|   associazioniIndividuali!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'categoria', nullable: true }) | ||||
|   categoria!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'email', nullable: true, length: 80 }) | ||||
|   email!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'fax', nullable: true, length: 80 }) | ||||
|   fax!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'telefono', nullable: true, length: 80 }) | ||||
|   telefono!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'sitoWeb', nullable: true, length: 80 }) | ||||
|   sitoWeb!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'riferimento', nullable: true, length: 80 }) | ||||
|   riferimento!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'comunicazioniFax', nullable: true }) | ||||
|   comunicazioniFax!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'comunicazioniEmail', nullable: true }) | ||||
|   comunicazioniEmail!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'comunicazioniSito', nullable: true }) | ||||
|   comunicazioniSito!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'comunicazioniPosta', nullable: true }) | ||||
|   comunicazioniPosta!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'gestionePreventiviWEB', nullable: true }) | ||||
|   gestionePreventiviWeb!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'codiceDocumentoWEB', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   codiceDocumentoWeb!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'numeroGiorniEffettuareRimborsi', nullable: true }) | ||||
|   numeroGiorniEffettuareRimborsi!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'numeroGiorniIntegrazioneDocumenti', nullable: true }) | ||||
|   numeroGiorniIntegrazioneDocumenti!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'gestioneIndividuali', nullable: true }) | ||||
|   gestioneIndividuali!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'fondoPluriaziendale', nullable: true }) | ||||
|   fondoPluriaziendale!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'emailCertificata', nullable: true, length: 80 }) | ||||
|   emailCertificata!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'comunicazioniEmailCertificata', nullable: true }) | ||||
|   comunicazioniEmailCertificata!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataAssunzioneFamiliari', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataAssunzioneFamiliari!: string | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'coperturaPreesistenzeAderentiNuoveAssunzioni', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   coperturaPreesistenzeAderentiNuoveAssunzioni!: number | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'coperturaPreesistenzeFamiliariNuoveAssunzioni', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   coperturaPreesistenzeFamiliariNuoveAssunzioni!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'noCessazioneAutomaticaFineAnno', nullable: true }) | ||||
|   noCessazioneAutomaticaFineAnno!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'contrattoRateSemestrali', nullable: true }) | ||||
|   contrattoRateSemestrali!: number | null; | ||||
| 
 | ||||
|   @Column('decimal', { | ||||
|     name: 'moraRitardoPagamenti', | ||||
|     nullable: true, | ||||
|     precision: 18, | ||||
|     scale: 2, | ||||
|   }) | ||||
|   moraRitardoPagamenti!: number | null; | ||||
| 
 | ||||
|   @Column('decimal', { | ||||
|     name: 'quotaRichiestaFondo', | ||||
|     nullable: true, | ||||
|     precision: 18, | ||||
|     scale: 2, | ||||
|   }) | ||||
|   quotaRichiestaFondo!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'percQuotaRichiestaFondo', nullable: true }) | ||||
|   percQuotaRichiestaFondo!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'scontoIndividuali', nullable: true }) | ||||
|   scontoIndividuali!: number | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'modalitaCalcoloDatiContributiviIndividuali', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   modalitaCalcoloDatiContributiviIndividuali!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceFiguraContratto', nullable: true }) | ||||
|   codiceFiguraContratto!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloTesserini', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloTesserini!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLettera', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloLettera!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'ignoraControlloPagamentiTitolare', nullable: true }) | ||||
|   ignoraControlloPagamentiTitolare!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'ignoraControlloPagamentiAzienda', nullable: true }) | ||||
|   ignoraControlloPagamentiAzienda!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codicePromoter', nullable: true }) | ||||
|   codicePromoter!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceOriginePromoter', nullable: true }) | ||||
|   codiceOriginePromoter!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'fornituraTesserino', nullable: true }) | ||||
|   fornituraTesserino!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataChiusuraContratto', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataChiusuraContratto!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLetteraRID', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloLetteraRid!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLetteraAdesione', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloLetteraAdesione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLetteraAdeguamenti', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloLetteraAdeguamenti!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'coopersalute', nullable: true }) | ||||
|   coopersalute!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'forzaEstensioneTolleranza', nullable: true }) | ||||
|   forzaEstensioneTolleranza!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'giorniEstensioneTolleranza', nullable: true }) | ||||
|   giorniEstensioneTolleranza!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLetteraSollecito', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloLetteraSollecito!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLetteraSollecito2', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloLetteraSollecito2!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLetteraNeonato', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   nomeModelloLetteraNeonato!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'comunicazioniViaEmail', nullable: true }) | ||||
|   comunicazioniViaEmail!: number | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'modalitaCalcoloQuotaAssociativaFondo', | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   modalitaCalcoloQuotaAssociativaFondo!: number; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'modalitaCalcoloQuotaAssociativaNumeroFondo', | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   modalitaCalcoloQuotaAssociativaNumeroFondo!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'quotaAssociativaFondo', default: () => '(0)' }) | ||||
|   quotaAssociativaFondo!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'tipoAnamnesi', default: () => '(1)' }) | ||||
|   tipoAnamnesi!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'primoCheck', default: () => '(1)' }) | ||||
|   primoCheck!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'secondoCheck', default: () => '(1)' }) | ||||
|   secondoCheck!: number; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'testoPrimoCheck', default: () => "''" }) | ||||
|   testoPrimoCheck!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'testoSecondoCheck', default: () => "''" }) | ||||
|   testoSecondoCheck!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'calcolaQuotaWEB', default: () => '(1)' }) | ||||
|   calcolaQuotaWeb!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'verificaEsistenzaOnline', default: () => '(1)' }) | ||||
|   verificaEsistenzaOnline!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'titolareGiaAssociato', default: () => '(1)' }) | ||||
|   titolareGiaAssociato!: number; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'intestazionePrimaPaginaAdesione', | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   intestazionePrimaPaginaAdesione!: string; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'intestazioneUltimaPaginaAdesione', | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   intestazioneUltimaPaginaAdesione!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'importaComeAssociati', default: () => '(1)' }) | ||||
|   importaComeAssociati!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'importazioneAutomatica', default: () => '(1)' }) | ||||
|   importazioneAutomatica!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'stampaModuloAdesione', default: () => '(1)' }) | ||||
|   stampaModuloAdesione!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'terzoCheck', default: () => '(1)' }) | ||||
|   terzoCheck!: number; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'testoTerzoCheck', default: () => "''" }) | ||||
|   testoTerzoCheck!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'testoEmailPreventivo', default: () => "''" }) | ||||
|   testoEmailPreventivo!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'testoEmailAdesione', default: () => "''" }) | ||||
|   testoEmailAdesione!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'oggettoEmailPreventivo', default: () => "''" }) | ||||
|   oggettoEmailPreventivo!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'oggettoEmailAdesione', default: () => "''" }) | ||||
|   oggettoEmailAdesione!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeModelloLetteraStampaAdesione', | ||||
|     length: 255, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   nomeModelloLetteraStampaAdesione!: string; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'oggettoSecondaNotificaScadenzaPreventivo', | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoSecondaNotificaScadenzaPreventivo!: string; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'oggettoPrimaNotificaScadenzaPreventivo', | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoPrimaNotificaScadenzaPreventivo!: string; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'testoSecondaNotificaScadenzaPreventivo', | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   testoSecondaNotificaScadenzaPreventivo!: string; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'testoPrimaNotificaScadenzaPreventivo', | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   testoPrimaNotificaScadenzaPreventivo!: string; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceBancaContributi', default: () => '(0)' }) | ||||
|   codiceBancaContributi!: string; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceBancaRimborsi', default: () => '(0)' }) | ||||
|   codiceBancaRimborsi!: string; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'testataCertificazione', | ||||
|     nullable: true, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   testataCertificazione!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'codaCertificazione', default: () => "''" }) | ||||
|   codaCertificazione!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'testataRiepilogoRimborsi', default: () => "''" }) | ||||
|   testataRiepilogoRimborsi!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'adesioneInfraAnnuale', default: () => '(0)' }) | ||||
|   adesioneInfraAnnuale!: number; | ||||
| 
 | ||||
|   @ManyToOne(() => EnteEntity, (enteEntity) => enteEntity.contratti) | ||||
|   @JoinColumn([{ name: 'codiceEnte', referencedColumnName: 'codiceEnte' }]) | ||||
|   ente!: EnteEntity; | ||||
| 
 | ||||
|   @OneToMany(() => SocioContrattoEntity, (socioContrattoEntity) => socioContrattoEntity.contratto) | ||||
|   socioContratto!: SocioContrattoEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<ContrattoEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,87 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; | ||||
| import { ContrattoEntity } from './contratto.entity'; | ||||
| import { GruppoEntity } from './gruppo.entity'; | ||||
| 
 | ||||
| @Index('PK_ent', ['codiceEnte'], { unique: true }) | ||||
| @Entity('ente') | ||||
| export class EnteEntity { | ||||
|   @Column('bigint', { primary: true, name: 'codiceEnte' }) | ||||
|   codiceEnte!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'ragioneSociale', nullable: true, length: 255 }) | ||||
|   ragioneSociale!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'partitaIva', nullable: true, length: 25 }) | ||||
|   partitaIva!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceFiscale', nullable: true, length: 25 }) | ||||
|   codiceFiscale!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteEnte', nullable: true }) | ||||
|   noteEnte!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'codiceDivisioneEsolver', | ||||
|     nullable: true, | ||||
|     length: 25, | ||||
|   }) | ||||
|   codiceDivisioneEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'enteAttivo', nullable: true }) | ||||
|   enteAttivo!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'indirizzoEnte', nullable: true, length: 80 }) | ||||
|   indirizzoEnte!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'numeroCivicoEnte', nullable: true, length: 80 }) | ||||
|   numeroCivicoEnte!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'capEnte', nullable: true, length: 10 }) | ||||
|   capEnte!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'localitaEnte', nullable: true, length: 80 }) | ||||
|   localitaEnte!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteCreazione', nullable: true }) | ||||
|   utenteCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataCreazione', nullable: true, length: 10 }) | ||||
|   dataCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraCreazione', nullable: true, length: 8 }) | ||||
|   oraCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteModifica', nullable: true }) | ||||
|   utenteModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataModifica', nullable: true, length: 10 }) | ||||
|   dataModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraModifica', nullable: true, length: 8 }) | ||||
|   oraModifica!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceGruppo', nullable: true }) | ||||
|   codiceGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'statoEnte', nullable: true }) | ||||
|   statoEnte!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'provinciaEnte', nullable: true }) | ||||
|   provinciaEnte!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'inOsservazione', nullable: true }) | ||||
|   inOsservazione!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'sitoInternet', nullable: true, length: 255 }) | ||||
|   sitoInternet!: string | null; | ||||
| 
 | ||||
|   @ManyToOne(() => GruppoEntity, (gruppoEntity) => gruppoEntity.enti) | ||||
|   gruppo!: ContrattoEntity; | ||||
| 
 | ||||
|   @OneToMany(() => ContrattoEntity, (contrattoEntity) => contrattoEntity.ente) | ||||
|   contratti!: ContrattoEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<EnteEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,155 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; | ||||
| import { SocioFormeAssistenzaEntity } from './socioFormeAssistenza.entity'; | ||||
| 
 | ||||
| @Index('PK_formeAssistenza', ['codiceFda'], { unique: true }) | ||||
| @Entity('formeAssistenza') | ||||
| export class FormeAssistenzaEntity { | ||||
|   @Column('bigint', { name: 'codiceGruppo' }) | ||||
|   codiceGruppo!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'codiceFda' }) | ||||
|   codiceFda!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'descrizioneFda', nullable: true, length: 80 }) | ||||
|   descrizioneFda!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'attivaFda', nullable: true }) | ||||
|   attivaFda!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'fornituraTesserino', nullable: true }) | ||||
|   fornituraTesserino!: number | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteFda', nullable: true }) | ||||
|   noteFda!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteCreazione', nullable: true }) | ||||
|   utenteCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataCreazione', nullable: true, length: 10 }) | ||||
|   dataCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraCreazione', nullable: true, length: 8 }) | ||||
|   oraCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteModifica', nullable: true }) | ||||
|   utenteModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataModifica', nullable: true, length: 10 }) | ||||
|   dataModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraModifica', nullable: true, length: 8 }) | ||||
|   oraModifica!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'attivaDiretta', nullable: true }) | ||||
|   attivaDiretta!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'attivaIndiretta', nullable: true }) | ||||
|   attivaIndiretta!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'anniAdesione', nullable: true }) | ||||
|   anniAdesione!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'gestionePreventivo', nullable: true }) | ||||
|   gestionePreventivo!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'interoNucleoFamiliare', nullable: true }) | ||||
|   interoNucleoFamiliare!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'escludeDocRimborsi', nullable: true }) | ||||
|   escludeDocRimborsi!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'struttureDirettaIncluse', nullable: true }) | ||||
|   struttureDirettaIncluse!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'struttureIndirettaIncluse', nullable: true }) | ||||
|   struttureIndirettaIncluse!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'categorieDirettaIncluse', nullable: true }) | ||||
|   categorieDirettaIncluse!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'categorieIndirettaIncluse', nullable: true }) | ||||
|   categorieIndirettaIncluse!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'descrizioneTesserino', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   descrizioneTesserino!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceFdaOrigine', nullable: true }) | ||||
|   codiceFdaOrigine!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataInizioValidita', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataInizioValidita!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataFineValidita', nullable: true, length: 10 }) | ||||
|   dataFineValidita!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'limiteMassimoAssistenza', nullable: true }) | ||||
|   limiteMassimoAssistenza!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'limiteMinimoAssistenza', nullable: true }) | ||||
|   limiteMinimoAssistenza!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'limiteEtaAderentiDa', nullable: true }) | ||||
|   limiteEtaAderentiDa!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'limiteEtaFamiliariDa', nullable: true }) | ||||
|   limiteEtaFamiliariDa!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'limiteEtaAderentiA', nullable: true }) | ||||
|   limiteEtaAderentiA!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'limiteEtaFamiliariA', nullable: true }) | ||||
|   limiteEtaFamiliariA!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'descrizioneLettera', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   descrizioneLettera!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'fdaOpzionale', nullable: true }) | ||||
|   fdaOpzionale!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'giorniCarenzaTesserino', nullable: true }) | ||||
|   giorniCarenzaTesserino!: number | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'informazioni', nullable: true }) | ||||
|   informazioni!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'denominazioneRegolamento', | ||||
|     nullable: true, | ||||
|     length: 1024, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   denominazioneRegolamento!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataMassimaAdesione', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|     default: () => "'9998-12-31'", | ||||
|   }) | ||||
|   dataMassimaAdesione!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'prioritaWeb', default: () => '(0)' }) | ||||
|   prioritaWeb!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'attivaDirettaAreaRiservata', default: () => '(0)' }) | ||||
|   attivaDirettaAreaRiservata!: number; | ||||
| 
 | ||||
|   @OneToMany(() => SocioFormeAssistenzaEntity, (socioFormeAssistenzaEntity) => socioFormeAssistenzaEntity.formaAssistenza) | ||||
|   socioFormeAssistenza!: SocioFormeAssistenzaEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<FormeAssistenzaEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,660 @@ | ||||
| import { Column, Entity, Index, OneToMany } from 'typeorm'; | ||||
| import { EnteEntity } from './ente.entity'; | ||||
| 
 | ||||
| @Index('PK_gruppo', ['codiceGruppo'], { unique: true }) | ||||
| @Entity('gruppo') | ||||
| export class GruppoEntity { | ||||
| 
 | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'codiceGruppo' }) | ||||
|   codiceGruppo!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'gruppo', nullable: true, length: 80 }) | ||||
|   gruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'descrizioneGruppo', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|   }) | ||||
|   descrizioneGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteGruppo', nullable: true }) | ||||
|   noteGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'referenteGruppo', nullable: true, length: 80 }) | ||||
|   referenteGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'telefonoReferente', nullable: true, length: 80 }) | ||||
|   telefonoReferente!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'cellulareReferente', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   cellulareReferente!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'emailReferente', nullable: true, length: 80 }) | ||||
|   emailReferente!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'indirizzoGruppo', nullable: true, length: 80 }) | ||||
|   indirizzoGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'numeroCivicoGruppo', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   numeroCivicoGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'capGruppo', nullable: true, length: 10 }) | ||||
|   capGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'localitaGruppo', nullable: true, length: 80 }) | ||||
|   localitaGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'telefono1Gruppo', nullable: true, length: 80 }) | ||||
|   telefono1Gruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'telefono2Gruppo', nullable: true, length: 80 }) | ||||
|   telefono2Gruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'faxGruppo', nullable: true, length: 80 }) | ||||
|   faxGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'emailGruppo', nullable: true, length: 80 }) | ||||
|   emailGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'sitoWebGruppo', nullable: true, length: 80 }) | ||||
|   sitoWebGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'gruppoAttivo', nullable: true }) | ||||
|   gruppoAttivo!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'esolverAttivo', nullable: true }) | ||||
|   esolverAttivo!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'serverEsolver', nullable: true, length: 80 }) | ||||
|   serverEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'databaseEsolver', nullable: true, length: 80 }) | ||||
|   databaseEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'usernameEsolver', nullable: true, length: 80 }) | ||||
|   usernameEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'passwordEsolver', nullable: true, length: 80 }) | ||||
|   passwordEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'gruppoEsolver', nullable: true, length: 80 }) | ||||
|   gruppoEsolver!: string | null; | ||||
| 
 | ||||
|   @Column('image', { name: 'logoGruppo', nullable: true }) | ||||
|   logoGruppo!: Buffer | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteCreazione', nullable: true }) | ||||
|   utenteCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataCreazione', nullable: true, length: 10 }) | ||||
|   dataCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraCreazione', nullable: true, length: 8 }) | ||||
|   oraCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteModifica', nullable: true }) | ||||
|   utenteModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataModifica', nullable: true, length: 10 }) | ||||
|   dataModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraModifica', nullable: true, length: 8 }) | ||||
|   oraModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'direttivaReport', nullable: true, length: 512 }) | ||||
|   direttivaReport!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'direttivaReportPers', | ||||
|     nullable: true, | ||||
|     length: 512, | ||||
|   }) | ||||
|   direttivaReportPers!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'statoGruppo', nullable: true }) | ||||
|   statoGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'provinciaGruppo', nullable: true }) | ||||
|   provinciaGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'fondo', nullable: true }) | ||||
|   fondo!: number | null; | ||||
| 
 | ||||
|   @Column('decimal', { | ||||
|     name: 'tassaIscrizione', | ||||
|     nullable: true, | ||||
|     precision: 18, | ||||
|     scale: 2, | ||||
|   }) | ||||
|   tassaIscrizione!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceFiscale', nullable: true, length: 80 }) | ||||
|   codiceFiscale!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'serverFTP', nullable: true, length: 200 }) | ||||
|   serverFtp!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'usernameFTP', nullable: true, length: 200 }) | ||||
|   usernameFtp!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'passwordFTP', nullable: true, length: 200 }) | ||||
|   passwordFtp!: string | null; | ||||
| 
 | ||||
|   @Column('decimal', { | ||||
|     name: 'massimalePresaCarico', | ||||
|     nullable: true, | ||||
|     precision: 18, | ||||
|     scale: 2, | ||||
|   }) | ||||
|   massimalePresaCarico!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'elencoLivello1', nullable: true, length: 80 }) | ||||
|   elencoLivello1!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'testoEmailPreventivo', nullable: true }) | ||||
|   testoEmailPreventivo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'emailPreventivoMittente', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   emailPreventivoMittente!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'emailPreventivoNotifica', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   emailPreventivoNotifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'desPreventivoMittente', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   desPreventivoMittente!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'desPreventivoNotifica', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   desPreventivoNotifica!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceContrattoIndividuali', nullable: true }) | ||||
|   codiceContrattoIndividuali!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'codiceAziendaBollettini', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   codiceAziendaBollettini!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'rigaTesserino1', nullable: true, length: 80 }) | ||||
|   rigaTesserino1!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'rigaTesserino2', nullable: true, length: 80 }) | ||||
|   rigaTesserino2!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'rigaTesserino3', nullable: true, length: 80 }) | ||||
|   rigaTesserino3!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'cartellaCondivisa', | ||||
|     nullable: true, | ||||
|     length: 512, | ||||
|   }) | ||||
|   cartellaCondivisa!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'serverSMTP', nullable: true, length: 80 }) | ||||
|   serverSmtp!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'usernameSMTP', nullable: true, length: 80 }) | ||||
|   usernameSmtp!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'passwordSMTP', nullable: true, length: 80 }) | ||||
|   passwordSmtp!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'emailIBAN', nullable: true, length: 255 }) | ||||
|   emailIban!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'emailFatture', nullable: true, length: 255 }) | ||||
|   emailFatture!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'emailRA', nullable: true, length: 255 }) | ||||
|   emailRa!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'emailAreaRiservataSoci', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   emailAreaRiservataSoci!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'desAreaRiservataSoci', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   desAreaRiservataSoci!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'usernameSMS', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   usernameSms!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'passwordSMS', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   passwordSms!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittenteSMS', | ||||
|     nullable: true, | ||||
|     length: 255, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   mittenteSms!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'sitoWebDati', length: 255, default: () => "''" }) | ||||
|   sitoWebDati!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'identificativoCreditore', | ||||
|     length: 35, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   identificativoCreditore!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'SMSProtocollo', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   smsProtocollo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailBonifico', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailBonifico!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'emailBonifico', | ||||
|     nullable: true, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   emailBonifico!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'emailAssegno', | ||||
|     nullable: true, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   emailAssegno!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailAssegno', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailAssegno!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'SMSRimborsoBonifico', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   smsRimborsoBonifico!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'SMSRimborsoAssegno', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   smsRimborsoAssegno!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'emailPIC', nullable: true, length: 255 }) | ||||
|   emailPic!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'stelline', length: 10, default: () => "''" }) | ||||
|   stelline!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'mittentePIC', length: 400, default: () => "''" }) | ||||
|   mittentePic!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'oggettoPIC', default: () => "''" }) | ||||
|   oggettoPic!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'messaggioPIC', default: () => "''" }) | ||||
|   messaggioPic!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'nomeSupportoContrattoPosta', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   nomeSupportoContrattoPosta!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'codiceContrattoPosta', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   codiceContrattoPosta!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'passwordContrattoPosta', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   passwordContrattoPosta!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { | ||||
|     name: 'codiceSpedizione', | ||||
|     nullable: true, | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   codiceSpedizione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailBonificoDomiciliato', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailBonificoDomiciliato!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'emailBonificoDomiciliato', | ||||
|     nullable: true, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   emailBonificoDomiciliato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'SMSRimborsoBonificoDomiciliato', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   smsRimborsoBonificoDomiciliato!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'notifichePush', default: () => '(0)' }) | ||||
|   notifichePush!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'controlloCodiceFiscale', default: () => '(-1)' }) | ||||
|   controlloCodiceFiscale!: number; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'emailSuperamentoPIC', default: () => "''" }) | ||||
|   emailSuperamentoPic!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'SMSProtocolloNotificaEsplicita', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   smsProtocolloNotificaEsplicita!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittenteSportello', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   mittenteSportello!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailBonificoDomiciliatoSportello', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailBonificoDomiciliatoSportello!: string; | ||||
| 
 | ||||
|   @Column('ntext', { | ||||
|     name: 'emailBonificoDomiciliatoSportello', | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   emailBonificoDomiciliatoSportello!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailBonificoSportello', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailBonificoSportello!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'emailBonificoSportello', default: () => "''" }) | ||||
|   emailBonificoSportello!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailSospensioneSportello', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailSospensioneSportello!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'emailSospensioneSportello', default: () => "''" }) | ||||
|   emailSospensioneSportello!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailRespingimentoSportello', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailRespingimentoSportello!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'emailRespingimentoSportello', default: () => "''" }) | ||||
|   emailRespingimentoSportello!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailSospensione', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailSospensione!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'emailSospensione', default: () => "''" }) | ||||
|   emailSospensione!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailRespingimento', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailRespingimento!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'emailRespingimento', default: () => "''" }) | ||||
|   emailRespingimento!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentePICdisdetta', | ||||
|     nullable: true, | ||||
|     length: 400, | ||||
|   }) | ||||
|   mittentePiCdisdetta!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'oggettoPICdisdetta', nullable: true }) | ||||
|   oggettoPiCdisdetta!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'messaggioPICdisdetta', nullable: true }) | ||||
|   messaggioPiCdisdetta!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'modalitaMassimaleBonifico', default: () => '(0)' }) | ||||
|   modalitaMassimaleBonifico!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'portaSMTP', length: 10, default: () => "''" }) | ||||
|   portaSmtp!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'useSSL', default: () => '(0)' }) | ||||
|   useSsl!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'serverSMTPSpot', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   serverSmtpSpot!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'usernameSMTPSpot', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   usernameSmtpSpot!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'passwordSMTPSpot', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   passwordSmtpSpot!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'portaSMTPSpot', | ||||
|     length: 10, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   portaSmtpSpot!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'useSSLSpot', default: () => '(0)' }) | ||||
|   useSslSpot!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'cartellaPubblicazioneLocale', | ||||
|     length: 512, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   cartellaPubblicazioneLocale!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'portaFTP', default: () => '(0)' }) | ||||
|   portaFtp!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'cartellaAllegatiFTP', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   cartellaAllegatiFtp!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentiEmailNotificaEmissioneRimborsoSocio', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   mittentiEmailNotificaEmissioneRimborsoSocio!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentiEmailInvioAllegatiPraticaSocio', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   mittentiEmailInvioAllegatiPraticaSocio!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentiEmailRichiestaDatiBancariSocio', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   mittentiEmailRichiestaDatiBancariSocio!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentiEmailLettereRichiestaContributiRinnoviIndividuali', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   mittentiEmailLettereRichiestaContributiRinnoviIndividuali!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentiEmailLettereSoci', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   mittentiEmailLettereSoci!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentiEmailLettereStrutture', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   mittentiEmailLettereStrutture!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'mittentiEmailLettereContratti', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   mittentiEmailLettereContratti!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'emailNotificaCodiciFiscaliDoppiMittente', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   emailNotificaCodiciFiscaliDoppiMittente!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'emailNotificaCodiciFiscaliDoppiDestinatari', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   emailNotificaCodiciFiscaliDoppiDestinatari!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'emailNotificaCodiciFiscaliDoppiCc', | ||||
|     nullable: true, | ||||
|     length: 200, | ||||
|   }) | ||||
|   emailNotificaCodiciFiscaliDoppiCc!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'oggettoEmailRichiestaDatiBancari', | ||||
|     length: 200, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   oggettoEmailRichiestaDatiBancari!: string; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'emailRichiestaDatiBancari', default: () => "''" }) | ||||
|   emailRichiestaDatiBancari!: string; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'flagAllegaModuloIbanEmailRichiestaDatiBancari', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   flagAllegaModuloIbanEmailRichiestaDatiBancari!: number | null; | ||||
| 
 | ||||
|   @OneToMany(() => EnteEntity, (enteEntity) => enteEntity.gruppo) | ||||
|   enti!: EnteEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<GruppoEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| export * from './API_CAP_Luoghi.entity'; | ||||
| export * from './API_Luoghi.entity'; | ||||
| export * from './API_Luoghi_Estero.entity'; | ||||
| export * from './allegati.entity'; | ||||
| export * from './contratto.entity'; | ||||
| export * from './ente.entity'; | ||||
| export * from './formeAssistenza.entity'; | ||||
| export * from './gruppo.entity'; | ||||
| export * from './missioni.entity'; | ||||
| export * from './socio.entity'; | ||||
| export * from './socioContratto.entity'; | ||||
| export * from './socioFormeAssistenza.entity'; | ||||
| export * from './sportelli.entity'; | ||||
| @ -0,0 +1,53 @@ | ||||
| import { Column, Entity, Index } from 'typeorm'; | ||||
| 
 | ||||
| @Index( | ||||
|   'PK_Missioni_Data', | ||||
|   [ | ||||
|     'codiceFiscale', | ||||
|     'tipoDocumento', | ||||
|     'idMissione', | ||||
|     'idLettera', | ||||
|     'idPratica', | ||||
|     'idAgenzia', | ||||
|     'dataInizio', | ||||
|     'dataFine', | ||||
|     'tipoContratto', | ||||
|   ], | ||||
|   { unique: true } | ||||
| ) | ||||
| @Entity('missioni') | ||||
| export class MissioniEntity { | ||||
|   @Column('nvarchar', { primary: true, name: 'codiceFiscale', length: 20 }) | ||||
|   codiceFiscale!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'tipoDocumento' }) | ||||
|   tipoDocumento!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'idMissione' }) | ||||
|   idMissione!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'idLettera' }) | ||||
|   idLettera!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'idPratica' }) | ||||
|   idPratica!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'idAgenzia' }) | ||||
|   idAgenzia!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { primary: true, name: 'dataInizio', length: 10 }) | ||||
|   dataInizio!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { primary: true, name: 'dataFine', length: 10 }) | ||||
|   dataFine!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { primary: true, name: 'tipoContratto', length: 20 }) | ||||
|   tipoContratto!: string; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'progressivoRecord', default: () => '(0)' }) | ||||
|   progressivoRecord!: string; | ||||
| 
 | ||||
|   constructor(init?: Partial<MissioniEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,651 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; | ||||
| import { ApiLuoghiEntity } from './API_Luoghi.entity'; | ||||
| import { GruppoEntity } from './gruppo.entity'; | ||||
| import { SocioContrattoEntity } from './socioContratto.entity'; | ||||
| import { SocioFormeAssistenzaEntity } from './socioFormeAssistenza.entity'; | ||||
| 
 | ||||
| @Index('PK_socio', ['codiceSocio'], { unique: true }) | ||||
| @Entity('socio') | ||||
| export class SocioEntity { | ||||
|   @Column('bigint', { name: 'codiceGruppo', nullable: true }) | ||||
|   codiceGruppo!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'codiceSocio' }) | ||||
|   codiceSocio!: string; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'matricolaSocio', nullable: true }) | ||||
|   matricolaSocio!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'cognome', nullable: true, length: 80 }) | ||||
|   cognome!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'nome', nullable: true, length: 80 }) | ||||
|   nome!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataNascita', nullable: true, length: 10 }) | ||||
|   dataNascita!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceFiscale', nullable: true, length: 20 }) | ||||
|   codiceFiscale!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceContrattoOld', nullable: true }) | ||||
|   codiceContrattoOld!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'socio.codiceFiguraContrattoOld', nullable: true }) | ||||
|   socioCodiceFiguraContrattoOld!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceRivenditore', nullable: true }) | ||||
|   codiceRivenditore!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteCreazione', nullable: true }) | ||||
|   utenteCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataCreazione', nullable: true, length: 10 }) | ||||
|   dataCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraCreazione', nullable: true, length: 8 }) | ||||
|   oraCreazione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'utenteModifica', nullable: true }) | ||||
|   utenteModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataModifica', nullable: true, length: 10 }) | ||||
|   dataModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'oraModifica', nullable: true, length: 8 }) | ||||
|   oraModifica!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'residenzaIndirizzo', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   residenzaIndirizzo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'residenzaNumeroCivico', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   residenzaNumeroCivico!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'residenzaLocalita', nullable: true, length: 80 }) | ||||
|   residenzaLocalita!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'residenzaCAP', nullable: true, length: 10 }) | ||||
|   residenzaCap!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'residenzaProvincia', nullable: true }) | ||||
|   residenzaProvincia!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'residenzaStato', nullable: true }) | ||||
|   residenzaStato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'domicilioIndirizzo', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   domicilioIndirizzo!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'domicilioNumeroCivico', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   domicilioNumeroCivico!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'domicilioLocalita', nullable: true, length: 80 }) | ||||
|   domicilioLocalita!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'domicilioCAP', nullable: true, length: 10 }) | ||||
|   domicilioCap!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'domicilioProvincia', nullable: true }) | ||||
|   domicilioProvincia!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'domicilioStato', nullable: true }) | ||||
|   domicilioStato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'nascitaLocalita', nullable: true, length: 80 }) | ||||
|   nascitaLocalita!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'nascitaCAP', nullable: true, length: 10 }) | ||||
|   nascitaCap!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'nascitaProvincia', nullable: true }) | ||||
|   nascitaProvincia!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'nascitaStato', nullable: true }) | ||||
|   nascitaStato!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'telefono', nullable: true, length: 80 }) | ||||
|   telefono!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'cellulare', nullable: true, length: 80 }) | ||||
|   cellulare!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'email', nullable: true, length: 80 }) | ||||
|   email!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceSesso', nullable: true }) | ||||
|   codiceSesso!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceStatoCivile', nullable: true }) | ||||
|   codiceStatoCivile!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceProfessione', nullable: true }) | ||||
|   codiceProfessione!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'consensoComunicazioneDati', nullable: true }) | ||||
|   consensoComunicazioneDati!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'consensoDiffusioneDati', nullable: true }) | ||||
|   consensoDiffusioneDati!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'riassicurato', nullable: true }) | ||||
|   riassicurato!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'fax', nullable: true, length: 80 }) | ||||
|   fax!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataIscrizione', nullable: true, length: 10 }) | ||||
|   dataIscrizione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataRichiestaRecesso', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataRichiestaRecesso!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataRecesso', nullable: true, length: 10 }) | ||||
|   dataRecesso!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'recessoAnnoSolareSuccessivo', nullable: true }) | ||||
|   recessoAnnoSolareSuccessivo!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'capofamiglia', nullable: true }) | ||||
|   flagCapofamiglia!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'livelloFamiliare', nullable: true }) | ||||
|   livelloFamiliare!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceCapofamiglia', nullable: true }) | ||||
|   codiceCapofamiglia!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codicePaese', nullable: true, length: 50 }) | ||||
|   codicePaese!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceCIN', nullable: true, length: 50 }) | ||||
|   codiceCin!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'checkDigitCIN', nullable: true, length: 50 }) | ||||
|   checkDigitCin!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'rifBancaEstero', nullable: true, length: 50 }) | ||||
|   rifBancaEstero!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'contoCorrente', nullable: true, length: 50 }) | ||||
|   contoCorrente!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceBIC', nullable: true, length: 50 }) | ||||
|   codiceBic!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceIBAN', nullable: true, length: 50 }) | ||||
|   codiceIban!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceABI', nullable: true, length: 50 }) | ||||
|   codiceAbi!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceCAB', nullable: true, length: 10 }) | ||||
|   codiceCab!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataDecesso', nullable: true, length: 10 }) | ||||
|   dataDecesso!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'modalitaSuddivisioneContributi', nullable: true }) | ||||
|   modalitaSuddivisioneContributi!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataEsclusione', nullable: true, length: 10 }) | ||||
|   dataEsclusione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceMotivoEsclusione', nullable: true }) | ||||
|   codiceMotivoEsclusione!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteEsclusione', nullable: true }) | ||||
|   noteEsclusione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'usernameWEB', nullable: true, length: 80 }) | ||||
|   usernameWeb!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'passwordWEB', nullable: true, length: 80 }) | ||||
|   passwordWeb!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'consensoNucleoFamiliare', nullable: true }) | ||||
|   consensoNucleoFamiliare!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webAccessoDisabilitato', nullable: true }) | ||||
|   webAccessoDisabilitato!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webModificaDati', nullable: true }) | ||||
|   webModificaDati!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webInfo', nullable: true }) | ||||
|   webInfo!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webStrutture', nullable: true }) | ||||
|   webStrutture!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webPratiche', nullable: true }) | ||||
|   webPratiche!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webCertificazione', nullable: true }) | ||||
|   webCertificazione!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webRimborsiBase', nullable: true }) | ||||
|   webRimborsiBase!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webRimborsiAvanzato', nullable: true }) | ||||
|   webRimborsiAvanzato!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webRichiestaPresaCarico', nullable: true }) | ||||
|   webRichiestaPresaCarico!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webServizioCup', nullable: true }) | ||||
|   webServizioCup!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'webEmailUsername', nullable: true }) | ||||
|   webEmailUsername!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'categoria1', nullable: true }) | ||||
|   categoria1!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'categoria2', nullable: true }) | ||||
|   categoria2!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'categoria3', nullable: true }) | ||||
|   categoria3!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'categoria4', nullable: true }) | ||||
|   categoria4!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'categoria5', nullable: true }) | ||||
|   categoria5!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'socioEsterno', nullable: true }) | ||||
|   socioEsterno!: number | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceMutua', nullable: true }) | ||||
|   codiceMutua!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataInizioValidita', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataInizioValidita!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataFineValidita', nullable: true, length: 10 }) | ||||
|   dataFineValidita!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'idAssiBase', nullable: true }) | ||||
|   idAssiBase!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'protezioniAssiBase', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   protezioniAssiBase!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'gruppoAssiBase', nullable: true, length: 80 }) | ||||
|   gruppoAssiBase!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteAnagrafica', nullable: true }) | ||||
|   noteAnagrafica!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'tipoSocio', nullable: true }) | ||||
|   tipoSocio!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'stampatoLibroSoci', default: () => '(0)' }) | ||||
|   stampatoLibroSoci!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataStampaLibroSoci', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataStampaLibroSoci!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'codiceModalitaPagamentoRimborsi', nullable: true }) | ||||
|   codiceModalitaPagamentoRimborsi!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'codiceModalitaVersamentoContributi', nullable: true }) | ||||
|   codiceModalitaVersamentoContributi!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataAssunzione', nullable: true, length: 10 }) | ||||
|   dataAssunzione!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceMotivoRecesso', nullable: true }) | ||||
|   codiceMotivoRecesso!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'codiceCanaleComunicazione', nullable: true }) | ||||
|   codiceCanaleComunicazione!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataRegistrazione', nullable: true, length: 10 }) | ||||
|   dataRegistrazione!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteAnamnesi', nullable: true }) | ||||
|   noteAnamnesi!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { | ||||
|     name: 'codiceModalitacessazioneContributiInfrannuale', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   codiceModalitacessazioneContributiInfrannuale!: string | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'forzaCodiceModalitacessazioneContributiInfrannuale', | ||||
|     nullable: true, | ||||
|   }) | ||||
|   forzaCodiceModalitacessazioneContributiInfrannuale!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'matricolaAziendale', | ||||
|     nullable: true, | ||||
|     length: 80, | ||||
|   }) | ||||
|   matricolaAziendale!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'coniugeCarico', nullable: true }) | ||||
|   coniugeCarico!: number | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteProtezioni', nullable: true }) | ||||
|   noteProtezioni!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'idNucleo', nullable: true }) | ||||
|   idNucleo!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'idSocio', nullable: true }) | ||||
|   idSocio!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'idLibroSoci', nullable: true }) | ||||
|   idLibroSoci!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'idPersona', nullable: true }) | ||||
|   idPersona!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codicePromoter', nullable: true }) | ||||
|   codicePromoter!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'originePromoter', nullable: true }) | ||||
|   originePromoter!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'livelloFamiliareAnagrafica', nullable: true }) | ||||
|   livelloFamiliareAnagrafica!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceCapoFamigliaAnagrafica', nullable: true }) | ||||
|   codiceCapoFamigliaAnagrafica!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'codiceModalitaVersamentoRID', nullable: true }) | ||||
|   codiceModalitaVersamentoRid!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataPossibileAmmissione', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataPossibileAmmissione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'banca', nullable: true, length: 255 }) | ||||
|   banca!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'agenziaBanca', nullable: true, length: 255 }) | ||||
|   agenziaBanca!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'intestazioneCC', nullable: true, length: 255 }) | ||||
|   intestazioneCc!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataRicezioneRichiestaRecesso', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   dataRicezioneRichiestaRecesso!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataUscitaNucleo', nullable: true, length: 10 }) | ||||
|   dataUscitaNucleo!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'datiBancariDaAggiornare', nullable: true }) | ||||
|   datiBancariDaAggiornare!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'telefono2', nullable: true, length: 80 }) | ||||
|   telefono2!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataModificaIban', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   dataModificaIban!: string | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'richiestoIBAN', | ||||
|     nullable: true, | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   richiestoIban!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataRichiestaIBAN', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   dataRichiestaIban!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'comunicazioniViaEmail', nullable: true }) | ||||
|   comunicazioniViaEmail!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'esitoUltimaValidazione', nullable: true }) | ||||
|   esitoUltimaValidazione!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataOraUltimaValidazione', | ||||
|     nullable: true, | ||||
|     length: 50, | ||||
|   }) | ||||
|   dataOraUltimaValidazione!: string | null; | ||||
| 
 | ||||
|   @Column('ntext', { name: 'noteUltimaValidazione', nullable: true }) | ||||
|   noteUltimaValidazione!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'stampareLibroSoci', default: () => '(0)' }) | ||||
|   stampareLibroSoci!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataStampaCessazioneLibroSoci', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|     default: () => "'9998-12-31'", | ||||
|   }) | ||||
|   dataStampaCessazioneLibroSoci!: string | null; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'stampataCessazioneLibroSoci', | ||||
|     nullable: true, | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   stampataCessazioneLibroSoci!: number | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'adesioneWeb', default: () => '(0)' }) | ||||
|   adesioneWeb!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'preeesistenzaContratto', default: () => '(0)' }) | ||||
|   preeesistenzaContratto!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'preeesistenzaInclusa', default: () => '(0)' }) | ||||
|   preeesistenzaInclusa!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'IBANRid', length: 50, default: () => "''" }) | ||||
|   ibanRid!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'intestatarioContoRid', | ||||
|     length: 255, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   intestatarioContoRid!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'dataMandato', | ||||
|     length: 10, | ||||
|     default: () => "'9998-12-31'", | ||||
|   }) | ||||
|   dataMandato!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'idMandato', length: 10, default: () => "''" }) | ||||
|   idMandato!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'codiceSWIFT', length: 80, default: () => "''" }) | ||||
|   codiceSwift!: string; | ||||
| 
 | ||||
|   @Column('decimal', { | ||||
|     name: 'percentualeFamiliareACarico', | ||||
|     precision: 18, | ||||
|     scale: 2, | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   percentualeFamiliareACarico!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'familiareMultiplo', default: () => '(0)' }) | ||||
|   familiareMultiplo!: number; | ||||
| 
 | ||||
|   @Column('nvarchar', { | ||||
|     name: 'origineAggiornamento', | ||||
|     length: 20, | ||||
|     default: () => "''", | ||||
|   }) | ||||
|   origineAggiornamento!: string; | ||||
| 
 | ||||
|   @Column('int', { name: 'consensoNewsletter', default: () => '(0)' }) | ||||
|   consensoNewsletter!: number; | ||||
| 
 | ||||
|   @Column('int', { | ||||
|     name: 'flagRichiestaVariazioneAnagrafica', | ||||
|     default: () => '(0)', | ||||
|   }) | ||||
|   flagRichiestaVariazioneAnagrafica!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'flagPresenzaMorositaPrestiti', default: () => '(0)' }) | ||||
|   flagPresenzaMorositaPrestiti!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'flagBloccoAmministrativo', default: () => '(0)' }) | ||||
|   flagBloccoAmministrativo!: number; | ||||
| 
 | ||||
|   @Column('int', { name: 'codiceTitoloStudio', default: () => '(0)' }) | ||||
|   codiceTitoloStudio!: number; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'codiceLuogoDomicilio', | ||||
|     nullable: true, | ||||
|     length: 5, | ||||
|   }) | ||||
|   codiceLuogoDomicilio!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'codiceLuogoNascita', nullable: true, length: 5 }) | ||||
|   codiceLuogoNascita!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'codiceLuogoResidenza', | ||||
|     nullable: true, | ||||
|     length: 5, | ||||
|   }) | ||||
|   codiceLuogoResidenza!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'domicilioFrazione', nullable: true, length: 80 }) | ||||
|   domicilioFrazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'nascitaFrazione', nullable: true, length: 80 }) | ||||
|   nascitaFrazione!: string | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'residenzaFrazione', nullable: true, length: 80 }) | ||||
|   residenzaFrazione!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { name: 'codicePartner', nullable: true, length: 255 }) | ||||
|   codicePartner!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'documentoIdentitaCodice', | ||||
|     nullable: true, | ||||
|     length: 100, | ||||
|   }) | ||||
|   documentoIdentitaCodice!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'documentoIdentitaDataEmissione', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   documentoIdentitaDataEmissione!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'documentoIdentitaDataScadenza', | ||||
|     nullable: true, | ||||
|     length: 10, | ||||
|   }) | ||||
|   documentoIdentitaDataScadenza!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'documentoIdentitaEnteRilascio', | ||||
|     nullable: true, | ||||
|     length: 100, | ||||
|   }) | ||||
|   documentoIdentitaEnteRilascio!: string | null; | ||||
| 
 | ||||
|   @Column('varchar', { | ||||
|     name: 'documentoIdentitaComuneRilascio', | ||||
|     nullable: true, | ||||
|     length: 5, | ||||
|   }) | ||||
|   documentoIdentitaCodiceLuogoComuneRilascio!: string | null; | ||||
| 
 | ||||
|   @ManyToOne(() => SocioEntity, (socioEntity) => socioEntity.familiari) | ||||
|   @JoinColumn([{ name: 'codiceCapofamiglia', referencedColumnName: 'codiceSocio' }]) | ||||
|   capofamiglia!: SocioEntity; | ||||
| 
 | ||||
|   @OneToMany(() => SocioEntity, (socioEntity) => socioEntity.capofamiglia) | ||||
|   familiari!: SocioEntity[]; | ||||
| 
 | ||||
|   @ManyToOne(() => GruppoEntity) | ||||
|   @JoinColumn([{ name: 'codiceGruppo', referencedColumnName: 'codiceGruppo' }]) | ||||
|   gruppo!: GruppoEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => ApiLuoghiEntity) | ||||
|   @JoinColumn([{ name: 'codiceLuogoNascita', referencedColumnName: 'codice' }]) | ||||
|   luogoNascita!: ApiLuoghiEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => ApiLuoghiEntity) | ||||
|   @JoinColumn([{ name: 'codiceLuogoResidenza', referencedColumnName: 'codice' }]) | ||||
|   luogoResidenza!: ApiLuoghiEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => ApiLuoghiEntity) | ||||
|   @JoinColumn([{ name: 'codiceLuogoDomicilio', referencedColumnName: 'codice' }]) | ||||
|   luogoDomicilio!: ApiLuoghiEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => ApiLuoghiEntity) | ||||
|   @JoinColumn([{ name: 'documentoIdentitaComuneRilascio', referencedColumnName: 'codice' }]) | ||||
|   documentoIdentitaComuneRilascio!: ApiLuoghiEntity; | ||||
| 
 | ||||
|   @OneToMany(() => SocioContrattoEntity, (socioContrattoEntity) => socioContrattoEntity.socio) | ||||
|   socioContratto!: SocioContrattoEntity[]; | ||||
| 
 | ||||
|   @OneToMany(() => SocioFormeAssistenzaEntity, (socioFormeAssistenzaEntity) => socioFormeAssistenzaEntity.socio) | ||||
|   socioFormeAssistenzas!: SocioFormeAssistenzaEntity[]; | ||||
| 
 | ||||
|   constructor(init?: Partial<SocioEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,50 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm'; | ||||
| import { ContrattoEntity } from './contratto.entity'; | ||||
| import { SocioEntity } from './socio.entity'; | ||||
| 
 | ||||
| @Index( | ||||
|   'PK_socioContratto', | ||||
|   ['codiceSocio', 'codiceCapofamiglia', 'codiceContratto', 'dataInizio', 'dataFine', 'codiceFiguraContratto'], | ||||
|   { unique: true } | ||||
| ) | ||||
| @Entity('socioContratto') | ||||
| export class SocioContrattoEntity { | ||||
|   @Column('bigint', { primary: true, name: 'codiceSocio' }) | ||||
|   codiceSocio!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'codiceCapofamiglia' }) | ||||
|   codiceCapofamiglia!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { primary: true, name: 'dataInizio', length: 10 }) | ||||
|   dataInizio!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { primary: true, name: 'dataFine', length: 10 }) | ||||
|   dataFine!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'codiceContratto' }) | ||||
|   codiceContratto!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'contratto', length: 400 }) | ||||
|   nomeContratto!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'codiceFiguraContratto' }) | ||||
|   codiceFiguraContratto!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'figuraContratto', length: 400 }) | ||||
|   figuraContratto!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'note', length: 400 }) | ||||
|   note!: string; | ||||
| 
 | ||||
|   @ManyToOne(() => SocioEntity, (socioEntity) => socioEntity.socioContratto) | ||||
|   @JoinColumn([{ name: 'codiceSocio', referencedColumnName: 'codiceSocio' }]) | ||||
|   socio!: SocioEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => ContrattoEntity, (contrattoEntity) => contrattoEntity.socioContratto) | ||||
|   @JoinColumn([{ name: 'codiceContratto', referencedColumnName: 'codiceContratto' }]) | ||||
|   contratto!: ContrattoEntity; | ||||
| 
 | ||||
|   constructor(init?: Partial<SocioContrattoEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,46 @@ | ||||
| import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm'; | ||||
| import { FormeAssistenzaEntity } from './formeAssistenza.entity'; | ||||
| import { SocioEntity } from './socio.entity'; | ||||
| 
 | ||||
| @Index('PK_socioFormeAssistenza', ['codiceSocio', 'codiceFdA', 'dataInizio', 'dataFine', 'titolare'], { unique: true }) | ||||
| @Entity('socioFormeAssistenza') | ||||
| export class SocioFormeAssistenzaEntity { | ||||
|   @Column('bigint', { primary: true, name: 'codiceSocio' }) | ||||
|   codiceSocio!: string; | ||||
| 
 | ||||
|   @Column('bigint', { primary: true, name: 'codiceFdA' }) | ||||
|   codiceFdA!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { primary: true, name: 'dataInizio', length: 10 }) | ||||
|   dataInizio!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { primary: true, name: 'dataFine', length: 10 }) | ||||
|   dataFine!: string; | ||||
| 
 | ||||
|   @Column('int', { primary: true, name: 'titolare' }) | ||||
|   titolare!: number; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceContratto', nullable: true }) | ||||
|   codiceContratto!: string | null; | ||||
| 
 | ||||
|   @Column('bigint', { name: 'codiceFiguraContratto', nullable: true }) | ||||
|   codiceFiguraContratto!: string | null; | ||||
| 
 | ||||
|   @Column('int', { name: 'forzaEtaContributi', nullable: true }) | ||||
|   forzaEtaContributi!: number | null; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'dataRicezione', nullable: true, length: 10 }) | ||||
|   dataRicezione!: string | null; | ||||
| 
 | ||||
|   @ManyToOne(() => FormeAssistenzaEntity, (formeAssistenzaEntity) => formeAssistenzaEntity.socioFormeAssistenza) | ||||
|   @JoinColumn([{ name: 'codiceFdA', referencedColumnName: 'codiceFda' }]) | ||||
|   formaAssistenza!: FormeAssistenzaEntity; | ||||
| 
 | ||||
|   @ManyToOne(() => SocioEntity, (socioEntity) => socioEntity.socioFormeAssistenzas) | ||||
|   @JoinColumn([{ name: 'codiceSocio', referencedColumnName: 'codiceSocio' }]) | ||||
|   socio!: SocioEntity; | ||||
| 
 | ||||
|   constructor(init?: Partial<SocioFormeAssistenzaEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| import { Column, Entity, Index } from 'typeorm'; | ||||
| 
 | ||||
| @Index('PK_sportelli', ['codiceSportello'], { unique: true }) | ||||
| @Entity('sportelli') | ||||
| export class SportelliEntity { | ||||
|   @Column('nvarchar', { primary: true, name: 'codiceSportello', length: 255 }) | ||||
|   codiceSportello!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'denominazione', length: 255 }) | ||||
|   denominazione!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'email', length: 255 }) | ||||
|   email!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'password', length: 255 }) | ||||
|   password!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'indirizzo', length: 200, default: () => "''" }) | ||||
|   indirizzo!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'citta', length: 200, default: () => "''" }) | ||||
|   citta!: string; | ||||
| 
 | ||||
|   @Column('nvarchar', { name: 'provincia', length: 2, default: () => "''" }) | ||||
|   provincia!: string; | ||||
| 
 | ||||
|   constructor(init?: Partial<SportelliEntity>) { | ||||
|     Object.assign(this, init); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| export * from './entities'; | ||||
| @ -0,0 +1 @@ | ||||
| export const APP_DATASOURCES = Symbol('APP_DATASOURCES'); | ||||
							
								
								
									
										20
									
								
								apps/ebitemp-api/src/modules/database/database.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								apps/ebitemp-api/src/modules/database/database.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| import { Global, Module } from '@nestjs/common'; | ||||
| import { EbitempApiDatabaseModule } from './connections/ebitemp-api/database.module'; | ||||
| import { OceanoDatabaseModule } from './connections/oceano/database.module'; | ||||
| import { APP_DATASOURCES } from './database.constants'; | ||||
| import { dataSources } from './utils/typeorm-data-source-factory'; | ||||
| 
 | ||||
| const dataSourcesProvider = { | ||||
|   provide: APP_DATASOURCES, | ||||
|   useValue: dataSources, | ||||
| }; | ||||
| 
 | ||||
| const databaseModules = [EbitempApiDatabaseModule, OceanoDatabaseModule]; | ||||
| 
 | ||||
| @Global() | ||||
| @Module({ | ||||
|   imports: [...databaseModules], | ||||
|   providers: [dataSourcesProvider], | ||||
|   exports: [dataSourcesProvider, ...databaseModules], | ||||
| }) | ||||
| export class AppDatabaseModule {} | ||||
| @ -0,0 +1,57 @@ | ||||
| import { registerAs } from '@nestjs/config'; | ||||
| import { ConnectionString } from 'connection-string'; | ||||
| import { first } from 'lodash'; | ||||
| import { z } from 'zod'; | ||||
| 
 | ||||
| export const databaseSchema = z.object({ | ||||
|   connectionString: z.string(), | ||||
|   type: z.enum([ | ||||
|     'mysql', | ||||
|     'postgres', | ||||
|     'cockroachdb', | ||||
|     'sap', | ||||
|     'mariadb', | ||||
|     'sqlite', | ||||
|     'cordova', | ||||
|     'react-native', | ||||
|     'nativescript', | ||||
|     'sqljs', | ||||
|     'oracle', | ||||
|     'mssql', | ||||
|     'mongodb', | ||||
|     'aurora-mysql', | ||||
|     'aurora-postgres', | ||||
|     'expo', | ||||
|     'better-sqlite3', | ||||
|     'capacitor', | ||||
|     'spanner', | ||||
|   ]), | ||||
|   host: z.string(), | ||||
|   port: z.number().optional(), | ||||
|   username: z.string(), | ||||
|   password: z.string(), | ||||
|   database: z.string(), | ||||
|   secure: z.boolean(), | ||||
| }); | ||||
| export type DatabaseConfig = z.TypeOf<typeof databaseSchema>; | ||||
| 
 | ||||
| export const rawDatabaseSchema = z.object({ | ||||
|   connectionString: z.string(), | ||||
|   secure: z.boolean().default(true), | ||||
| }); | ||||
| export type RawDatabaseConfigSchema = z.TypeOf<typeof rawDatabaseSchema>; | ||||
| 
 | ||||
| export const databaseConfigFactory = (opts: RawDatabaseConfigSchema) => registerAs('database', () => { | ||||
|   const connectionString = new ConnectionString(opts.connectionString); | ||||
|   const config: DatabaseConfig = databaseSchema.strict().parse({ | ||||
|     connectionString: connectionString.toString(), | ||||
|     type: connectionString.protocol, | ||||
|     host: first(connectionString.hosts)?.name, | ||||
|     port: first(connectionString.hosts)?.port, | ||||
|     username: connectionString.user, | ||||
|     password: connectionString.password, | ||||
|     database: first(connectionString.path), | ||||
|     secure: opts.secure, | ||||
|   }); | ||||
|   return config; | ||||
| }); | ||||
| @ -0,0 +1,17 @@ | ||||
| import { DataSource, DataSourceOptions } from 'typeorm'; | ||||
| 
 | ||||
| export const dataSources: DataSource[] = []; | ||||
| 
 | ||||
| export const typeormDataSourceFactory = (dataSources: DataSource[]) => async (options?: DataSourceOptions) => { | ||||
|   const dataSource = await new DataSource(options!).initialize(); | ||||
|   dataSources.push(dataSource); | ||||
|   return dataSource; | ||||
| }; | ||||
| 
 | ||||
| export const typeormTransactionalDataSourceFactory = | ||||
|   (name?: string) => async (options?: DataSourceOptions) => { | ||||
|     const tt = await import('typeorm-transactional'); | ||||
|     const dataSource = tt.addTransactionalDataSource({ name: name, dataSource: new DataSource(options!) }); | ||||
|     dataSources.push(dataSource); | ||||
|     return dataSource; | ||||
|   }; | ||||
| @ -0,0 +1,5 @@ | ||||
| export const typeormEntitiesFromImport = async <T extends object>(entities: T) => { | ||||
|   return (Object.keys(entities) as Array<keyof typeof entities>).map( | ||||
|     (entity: keyof typeof entities) => entities[entity] | ||||
|   ); | ||||
| }; | ||||
| @ -0,0 +1,26 @@ | ||||
| import { TypeOrmModuleOptions } from '@nestjs/typeorm'; | ||||
| import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; | ||||
| import { DatabaseConfig } from './database-config'; | ||||
| 
 | ||||
| export const typeormModuleOptionsFactory = async ( | ||||
|   databaseConfig: DatabaseConfig, | ||||
|   entities: any[], | ||||
|   name?: string | ||||
| ): Promise<TypeOrmModuleOptions> => { | ||||
|   return { | ||||
|     name: name, | ||||
|     type: databaseConfig.type as any, | ||||
|     host: databaseConfig.host, | ||||
|     port: databaseConfig.port, | ||||
|     username: databaseConfig.username, | ||||
|     password: databaseConfig.password, | ||||
|     database: databaseConfig.database, | ||||
|     entities: entities, | ||||
|     synchronize: false, | ||||
|     logging: process.env['NODE_ENV'] !== 'production', | ||||
|     namingStrategy: new SnakeNamingStrategy(), | ||||
|     options: { | ||||
|       ...(!databaseConfig.secure ? { trustServerCertificate: true } : {}), | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
| @ -0,0 +1,5 @@ | ||||
| 
 | ||||
| export async function patchTypeOrm() { | ||||
|   (await import('typeorm-transactional')).initializeTransactionalContext(); | ||||
|   (await import('typeorm-scoped')).patchSelectQueryBuilder(); | ||||
| }; | ||||
| @ -0,0 +1,105 @@ | ||||
| import { Inject, Injectable, OnApplicationBootstrap } from '@nestjs/common'; | ||||
| import { differenceBy, get } from 'lodash'; | ||||
| import { DataSource, QueryFailedError } from 'typeorm'; | ||||
| import { APP_DATASOURCES } from '../database/database.constants'; | ||||
| import { Enumify, registeredEnums } from './enumify'; | ||||
| import { EnumifyConfig, enumifyConfig } from './enumify.config'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class EnumifyUpdateFromDatabaseService implements OnApplicationBootstrap { | ||||
|   constructor( | ||||
|     @Inject(APP_DATASOURCES) private readonly datasources: DataSource[], | ||||
|     @Inject(enumifyConfig.KEY) private readonly config: EnumifyConfig | ||||
|   ) {} | ||||
| 
 | ||||
|   async onApplicationBootstrap() { | ||||
|     try { | ||||
|       const enumUpdateFromDbOnStart = this.config.shouldUpdateEnumFromDbOnStartup; | ||||
|       if (!enumUpdateFromDbOnStart) return; | ||||
| 
 | ||||
|       const errors = []; | ||||
| 
 | ||||
|       for (const dataSource of this.datasources) { | ||||
|         const logging = dataSource.options.logging; | ||||
|         dataSource.setOptions({ logging: false }); | ||||
|         for (const _registeredEnum of get(registeredEnums, dataSource.name, [])) { | ||||
|           const registeredEnum = _registeredEnum as typeof Enumify; | ||||
| 
 | ||||
|           const repo = dataSource.getRepository(registeredEnum); | ||||
| 
 | ||||
|           const enumValues = registeredEnum.enumValues; | ||||
|           let dbValues: Enumify[]; | ||||
|           try { | ||||
|             dbValues = await repo.find(); | ||||
|           } catch (err) { | ||||
|             if ( | ||||
|               err instanceof QueryFailedError && | ||||
|               err.message === `Error: Invalid object name '${repo.metadata.tableName}'.` | ||||
|             ) { | ||||
|               errors.push( | ||||
|                 `[${dataSource.name}] ${registeredEnum.name}: Table present in code but missing on database: ${enumValues}` | ||||
|               ); | ||||
|             } | ||||
|             if (err instanceof QueryFailedError && err.message.startsWith('Error: Invalid column name')) { | ||||
|               errors.push(`[${dataSource.name}] ${registeredEnum.name}: [${repo.metadata.tableName}] ${err.message}`); | ||||
|             } | ||||
|             continue; | ||||
|           } | ||||
| 
 | ||||
|           const differenceByDbValues = differenceBy(dbValues, enumValues, (x) => (x.id, x.nome)); | ||||
|           const differenceByEnumValues = differenceBy(enumValues, dbValues, (x) => (x.id, x.nome)); | ||||
|           if (differenceByDbValues.length > 0) { | ||||
|             errors.push( | ||||
|               `[${dataSource.name}] ${ | ||||
|                 registeredEnum.name | ||||
|               }: Values present on database but missing in code: ${differenceBy( | ||||
|                 dbValues, | ||||
|                 enumValues, | ||||
|                 (x) => (x.id, x.nome) | ||||
|               )}` | ||||
|             ); | ||||
|           } | ||||
|           if (differenceByEnumValues.length > 0) { | ||||
|             errors.push( | ||||
|               `[${dataSource.name}] ${ | ||||
|                 registeredEnum.name | ||||
|               }: Values present in code but missing in database: ${differenceBy( | ||||
|                 enumValues, | ||||
|                 dbValues, | ||||
|                 (x) => (x.id, x.nome) | ||||
|               )}` | ||||
|             ); | ||||
|           } | ||||
| 
 | ||||
|           for (const dbValue of dbValues) { | ||||
|             const valueOfByName = registeredEnum.fromKey(dbValue.nome!); | ||||
|             const keyOfByCode = registeredEnum.fromValue(dbValue.id!); | ||||
|             if (valueOfByName != null && dbValue.id != valueOfByName?.id) { | ||||
|               errors.push( | ||||
|                 `[${dataSource.name}] ${registeredEnum.name}: Different values between database (${dbValue.id}, ${dbValue.nome}) and code (${valueOfByName.id}, ${valueOfByName.nome})` | ||||
|               ); | ||||
|             } else if (keyOfByCode != null && dbValue.nome != keyOfByCode?.nome) { | ||||
|               errors.push( | ||||
|                 `[${dataSource.name}] ${registeredEnum.name}: Different values between database (${dbValue.id}, ${dbValue.nome}) and code (${keyOfByCode.id}, ${keyOfByCode.nome})` | ||||
|               ); | ||||
|             } else if (valueOfByName != null || keyOfByCode != null) { | ||||
|               const enumValue = (valueOfByName ?? keyOfByCode)!; | ||||
|               Object.assign(enumValue, dbValue); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         dataSource.setOptions({ logging: logging }); | ||||
|       } | ||||
| 
 | ||||
|       if (errors.length > 0) { | ||||
|         throw new Error(errors.join('\n\t* ')); | ||||
|       } | ||||
|     } catch (err) { | ||||
|       if (err instanceof Error) { | ||||
|         console.warn(err.message, EnumifyUpdateFromDatabaseService.name); | ||||
|       } else { | ||||
|         throw err; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										17
									
								
								apps/ebitemp-api/src/modules/enumify/enumify.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								apps/ebitemp-api/src/modules/enumify/enumify.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import coerceRecordTypes from '../config/utils/coerce-record-types'; | ||||
| import { registerAs } from '@nestjs/config'; | ||||
| import { z } from 'zod'; | ||||
| 
 | ||||
| export const enumifySchema = z.object({ | ||||
|   shouldUpdateEnumFromDbOnStartup: z.boolean().default(true), | ||||
| }); | ||||
| export type EnumifyConfig = z.infer<typeof enumifySchema>; | ||||
| 
 | ||||
| export const enumifyConfig = registerAs('enumify', () => { | ||||
|   const env = coerceRecordTypes(process.env); | ||||
| 
 | ||||
|   const config: EnumifyConfig = enumifySchema.strict().parse({ | ||||
|     shouldUpdateEnumFromDbOnStartup: env['ENUM_UPDATE_FROM_DB_ON_START'] | ||||
|   }); | ||||
|   return config; | ||||
| }); | ||||
							
								
								
									
										12
									
								
								apps/ebitemp-api/src/modules/enumify/enumify.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								apps/ebitemp-api/src/modules/enumify/enumify.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { Module, Global } from '@nestjs/common'; | ||||
| import { EnumifyUpdateFromDatabaseService } from './enumify-update-from-database.service'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| import { enumifyConfig } from './enumify.config'; | ||||
| 
 | ||||
| @Global() | ||||
| @Module({ | ||||
|   imports: [ConfigModule.forFeature(enumifyConfig)], | ||||
|   providers: [EnumifyUpdateFromDatabaseService], | ||||
|   exports: [EnumifyUpdateFromDatabaseService], | ||||
| }) | ||||
| export class EnumifyModule {} | ||||
							
								
								
									
										74
									
								
								apps/ebitemp-api/src/modules/enumify/enumify.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								apps/ebitemp-api/src/modules/enumify/enumify.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| // Based on https://github.com/rauschma/enumify
 | ||||
| // Original license: MIT License - Copyright (c) 2020 Axel Rauschmayer
 | ||||
| 
 | ||||
| export const registeredEnums: Record<string, typeof Enumify[]> = {}; | ||||
| export function Enum(schema: string) { | ||||
|   return function (target: typeof Enumify) { | ||||
|     registeredEnums[schema] ??= []; | ||||
|     registeredEnums[schema].push(target); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export class Enumify { | ||||
|   static enumKeys: Array<string>; | ||||
|   static enumValues: Array<Enumify>; | ||||
| 
 | ||||
|   public readonly id?: number; | ||||
|   public readonly nome?: string; | ||||
| 
 | ||||
|   static closeEnum() { | ||||
|     const enumKeys: Array<string> = []; | ||||
|     const enumValues: Array<Enumify> = []; | ||||
| 
 | ||||
|     for (const [key, value] of Object.entries(this)) { | ||||
|       value.nome ??= key; | ||||
|       value.id ??= enumValues.length; | ||||
| 
 | ||||
|       if (value.id == null || value.id === '') { | ||||
|         throw new Error(`${this.name}.id`); | ||||
|       } | ||||
|       if (value.nome == null || value.nome === '') { | ||||
|         throw new Error(`${this.name}.nome`); | ||||
|       } | ||||
| 
 | ||||
|       enumKeys.push(value.nome); | ||||
|       enumValues.push(value); | ||||
|     } | ||||
|     this.enumKeys = enumKeys; | ||||
|     this.enumValues = enumValues; | ||||
|   } | ||||
| 
 | ||||
|   static fromKey(key: string): undefined | Enumify { | ||||
|     if (this.enumKeys == undefined) { | ||||
|       throw new Error(`Did you forget to call static _ = ${this.name}.closeEnum() after setup?`); | ||||
|     } | ||||
| 
 | ||||
|     const index = this.enumKeys.findIndex((enumKey) => enumKey.toUpperCase() === key.toString().toUpperCase()); | ||||
|     if (index >= 0) { | ||||
|       return this.enumValues[index]; | ||||
|     } | ||||
|     return undefined; | ||||
|   } | ||||
| 
 | ||||
|   static fromValue(value: number): undefined | Enumify { | ||||
|     if (this.enumValues == undefined) { | ||||
|       throw new Error(`Did you forget to call static _ = ${this.name}.closeEnum() after setup?`); | ||||
|     } | ||||
| 
 | ||||
|     const index = this.enumValues.map((x) => x.id).indexOf(value); | ||||
|     if (index >= 0) { | ||||
|       const key = this.enumKeys[index]; | ||||
|       return this.fromKey(key); | ||||
|     } | ||||
|     return undefined; | ||||
|   } | ||||
| 
 | ||||
|   protected constructor(id?: number, nome?: string) { | ||||
|     this.id = id; | ||||
|     this.nome = nome; | ||||
|   } | ||||
| 
 | ||||
|   toString() { | ||||
|     return `(${this.id}, ${this.nome})`; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,137 @@ | ||||
| import { Logger } from '@nestjs/common'; | ||||
| import fs from 'node:fs'; | ||||
| import { randomUUID } from 'node:crypto'; | ||||
| import path from 'node:path'; | ||||
| import { FileTransactionsService } from './file-transactions.service'; | ||||
| import { | ||||
|   copyFilesToFolderOrFail, | ||||
|   deleteFiles, | ||||
|   renameFiles, | ||||
|   renameFilesOrFail, | ||||
| } from './file-utils'; | ||||
| import dayjs from 'dayjs'; | ||||
| 
 | ||||
| export class FileTransaction { | ||||
|   public static readonly TO_ADD_SUFFIX = 'toAdd'; | ||||
|   public static readonly TO_DELETE_SUFFIX = 'toDelete'; | ||||
| 
 | ||||
|   private readonly manager: FileTransactionsService; | ||||
|   private readonly logger: Logger; | ||||
| 
 | ||||
|   public readonly transactionId: string; | ||||
|   private readonly ts: Date; | ||||
|   public readonly filesToAdd: { src: string; dest: string }[]; | ||||
|   public readonly filesToDelete: string[]; | ||||
| 
 | ||||
|   constructor( | ||||
|     manager: FileTransactionsService, | ||||
|     opts: Partial<{ | ||||
|       transactionId: string; | ||||
|       filesToAdd: { src: string; dest: string }[]; | ||||
|       filesToDelete: string[]; | ||||
|       logger: Logger; | ||||
|     }> | ||||
|   ) { | ||||
|     this.manager = manager; | ||||
|     this.logger = opts.logger ?? new Logger(FileTransaction.name); | ||||
|     this.filesToAdd = opts.filesToAdd ?? []; | ||||
|     this.filesToDelete = opts.filesToDelete ?? []; | ||||
|     this.transactionId = opts.transactionId ?? randomUUID().split('-').pop() as string; | ||||
|     this.ts = new Date(); | ||||
|   } | ||||
| 
 | ||||
|   async addFile(src: string, dest: string) { | ||||
|     const destFolderName = path.dirname(dest); | ||||
|     const destFileName = path.basename(dest); | ||||
|     const destFileNameWithSuffix = `${destFileName}.${this.transactionId}.${FileTransaction.TO_ADD_SUFFIX}`; | ||||
|     const destFilePathWithSuffix = path.join( | ||||
|       destFolderName, | ||||
|       destFileNameWithSuffix | ||||
|     ); | ||||
| 
 | ||||
|     this.logger.log( | ||||
|       `Adding file '${destFilePathWithSuffix}' in fileTransaction '${ | ||||
|         this.transactionId | ||||
|       }' (started at ${dayjs(this.ts).format('YYYY-MM-DD HH:mm:ss')})` | ||||
|     ); | ||||
|     this.filesToAdd.push({ src: src, dest: destFilePathWithSuffix }); | ||||
| 
 | ||||
|     if (fs.existsSync(dest)) { | ||||
|       await this.deleteFile(dest); | ||||
|     } | ||||
| 
 | ||||
|     await copyFilesToFolderOrFail(this.logger, destFolderName, { | ||||
|       srcpath: src, | ||||
|       name: destFileNameWithSuffix, | ||||
|     }); | ||||
|     await deleteFiles(this.logger, src); | ||||
|     this.manager.persist(this); | ||||
|   } | ||||
| 
 | ||||
|   async deleteFile(src: string) { | ||||
|     const destFolderName = path.dirname(src); | ||||
|     const destFileName = path.basename(src); | ||||
|     const destFileNameWithSuffix = `${destFileName}.${this.transactionId}.${FileTransaction.TO_DELETE_SUFFIX}`; | ||||
|     const destFilePathWithSuffix = path.join( | ||||
|       destFolderName, | ||||
|       destFileNameWithSuffix | ||||
|     ); | ||||
| 
 | ||||
|     this.logger.log( | ||||
|       `Deleting file '${destFilePathWithSuffix}' in fileTransaction '${ | ||||
|         this.transactionId | ||||
|       }' (started at ${dayjs(this.ts).format('YYYY-MM-DD HH:mm:ss')})` | ||||
|     ); | ||||
|     this.filesToDelete.push(destFilePathWithSuffix); | ||||
|     await renameFilesOrFail(this.logger, { src, dest: destFilePathWithSuffix }); | ||||
|     this.manager.persist(this); | ||||
|   } | ||||
| 
 | ||||
|   async commitTransaction() { | ||||
|     this.logger.log( | ||||
|       `Committing fileTransaction '${this.transactionId}' (started at ${dayjs( | ||||
|         this.ts | ||||
|       ).format('YYYY-MM-DD HH:mm:ss')})` | ||||
|     ); | ||||
|     await renameFiles( | ||||
|       this.logger, | ||||
|       ...this.filesToAdd.map((fileToAdd) => ({ | ||||
|         src: fileToAdd.dest, | ||||
|         dest: fileToAdd.dest.replace( | ||||
|           `.${this.transactionId}.${FileTransaction.TO_ADD_SUFFIX}`, | ||||
|           '' | ||||
|         ), | ||||
|       })) | ||||
|     ); | ||||
|     await deleteFiles(this.logger, ...this.filesToDelete); | ||||
|     this.manager.unregister(this); | ||||
|   } | ||||
| 
 | ||||
|   async rollbackTransaction() { | ||||
|     this.logger.log( | ||||
|       `Rollbacking fileTransaction '${this.transactionId}' (started at ${dayjs( | ||||
|         this.ts | ||||
|       ).format('YYYY-MM-DD HH:mm:ss')})` | ||||
|     ); | ||||
| 
 | ||||
|     await renameFiles( | ||||
|       this.logger, | ||||
|       ...this.filesToAdd.map((fileToMove) => ({ | ||||
|         src: fileToMove.dest, | ||||
|         dest: fileToMove.src, | ||||
|       })) | ||||
|     ); | ||||
| 
 | ||||
|     await renameFiles( | ||||
|       this.logger, | ||||
|       ...this.filesToDelete.map((fileToRestore) => ({ | ||||
|         src: fileToRestore, | ||||
|         dest: fileToRestore.replace( | ||||
|           `.${this.transactionId}.${FileTransaction.TO_DELETE_SUFFIX}`, | ||||
|           '' | ||||
|         ), | ||||
|       })) | ||||
|     ); | ||||
|     this.manager.unregister(this); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| import { Global, Module } from '@nestjs/common'; | ||||
| import { KeyvModule } from '../keyv/keyv.module'; | ||||
| import { FileTransactionsService } from './file-transactions.service'; | ||||
| 
 | ||||
| @Global() | ||||
| @Module({ | ||||
|   imports: [KeyvModule], | ||||
|   providers: [FileTransactionsService], | ||||
|   exports: [FileTransactionsService], | ||||
| }) | ||||
| export class AppFileTransactionsModule {} | ||||
| @ -0,0 +1,54 @@ | ||||
| import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; | ||||
| import { FileTransaction } from './file-transaction'; | ||||
| import { createKeyv, Keyv } from '@keyv/redis'; | ||||
| import { parse, stringify } from 'flatted'; | ||||
| import { KeyvService } from '../keyv/keyv.service'; | ||||
| 
 | ||||
| const FILE_TRANSACTIONS_KEY = `FILE_TRANSACTIONS_KEY`; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class FileTransactionsService implements OnModuleInit { | ||||
|   private readonly logger = new Logger(FileTransactionsService.name); | ||||
|   private readonly map: Record<string, FileTransaction> = {}; | ||||
| 
 | ||||
|   private readonly keyv: Keyv; | ||||
| 
 | ||||
|   constructor(readonly keyvService: KeyvService) { | ||||
|     this.keyv = this.keyvService.create({ | ||||
|       namespace: FileTransactionsService.name, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   register(logger?: Logger) { | ||||
|     const tx = new FileTransaction(this, { logger }); | ||||
|     this.persist(tx); | ||||
|     return tx; | ||||
|   } | ||||
| 
 | ||||
|   persist(tx: FileTransaction) { | ||||
|     this.map[tx.transactionId] = tx; | ||||
|     this.keyv.set(FILE_TRANSACTIONS_KEY, this.map); | ||||
|   } | ||||
| 
 | ||||
|   unregister(tx: FileTransaction) { | ||||
|     delete this.map[tx.transactionId]; | ||||
|     this.keyv.set(FILE_TRANSACTIONS_KEY, this.map); | ||||
|   } | ||||
| 
 | ||||
|   async onModuleInit() { | ||||
|     this.logger.log('Checking for dangling file transactions...'); | ||||
|     const dangling = await this.keyv.get<Set<FileTransaction>>( | ||||
|       FILE_TRANSACTIONS_KEY | ||||
|     ); | ||||
| 
 | ||||
|     if (dangling) { | ||||
|       for (const danglingTx of Object.values(dangling)) { | ||||
|         const tx = new FileTransaction(this, { | ||||
|           ...danglingTx, | ||||
|           logger: this.logger, | ||||
|         }); | ||||
|         await tx.rollbackTransaction(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										106
									
								
								apps/ebitemp-api/src/modules/file-transactions/file-utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								apps/ebitemp-api/src/modules/file-transactions/file-utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | ||||
| import { Logger } from '@nestjs/common'; | ||||
| import { promises as fs } from 'node:fs'; | ||||
| import path from 'path'; | ||||
| 
 | ||||
| interface File { | ||||
|   srcpath: string; | ||||
|   name: string; | ||||
| } | ||||
| 
 | ||||
| export const copyFilesToFolder = (logger: Logger, destFolder: string, ...files: File[]) => | ||||
|   _copyFilesToFolder(logger, destFolder, false, ...files); | ||||
| export const copyFilesToFolderOrFail = (logger: Logger, destFolder: string, ...files: File[]) => | ||||
|   _copyFilesToFolder(logger, destFolder, true, ...files); | ||||
| export const deleteFolder = (logger: Logger, folder: string) => _deleteFolder(logger, false, folder); | ||||
| export const deleteFolderOrFail = (logger: Logger, folder: string) => _deleteFolder(logger, true, folder); | ||||
| export const deleteFiles = (logger: Logger, ...files: string[]) => _deleteFiles(logger, false, ...files); | ||||
| export const deleteFilesOrFail = (logger: Logger, ...files: string[]) => _deleteFiles(logger, true, ...files); | ||||
| export const renameFiles = (logger: Logger, ...files: Array<{ src: string; dest: string }>) => | ||||
|   _renameFiles(logger, false, ...files); | ||||
| export const renameFilesOrFail = (logger: Logger, ...files: Array<{ src: string; dest: string }>) => | ||||
|   _renameFiles(logger, true, ...files); | ||||
| 
 | ||||
| export const normalizeWin32PathToPosix = (filepath: string): string => | ||||
|   path.win32.normalize(filepath).split(path.win32.sep).join(path.posix.sep); | ||||
| 
 | ||||
| const _copyFilesToFolder = async ( | ||||
|   logger: Logger, | ||||
|   destFolder: string, | ||||
|   throwsOnError = false, | ||||
|   ...files: File[] | ||||
| ): Promise<void> => { | ||||
|   await fs.mkdir(destFolder, { recursive: true }); | ||||
| 
 | ||||
|   for (const file of files) { | ||||
|     try { | ||||
|       const destpath = path.join(destFolder, file.name); | ||||
|       await fs.copyFile(file.srcpath, destpath); | ||||
|       logger.debug(`Copying file '${file.srcpath}' to '${destpath}'`); | ||||
|     } catch (err: any) { | ||||
|       const msg = `Could not copy '${file.srcpath}' to '${destFolder}'. Inner exception: ${err.message}`; | ||||
|       if (throwsOnError) { | ||||
|         logger.error(msg); | ||||
|         err.message = msg; | ||||
|         throw err; | ||||
|       } | ||||
|       logger.warn(msg); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const _deleteFolder = async (logger: Logger, throwsOnError = false, folder: string): Promise<void> => { | ||||
|   try { | ||||
|     await fs.rmdir(folder, { recursive: true }); | ||||
|     logger.debug(`Deleting folder '${folder}'`); | ||||
|   } catch (err: any) { | ||||
|     if (err.code === 'ENOENT') { | ||||
|       return; | ||||
|     } | ||||
|     const msg = `Could not delete '${folder}'. Inner exception: ${err.message}`; | ||||
|     if (throwsOnError) { | ||||
|       logger.error(msg); | ||||
|       throw err; | ||||
|     } | ||||
|     logger.warn(msg); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const _deleteFiles = async (logger: Logger, throwsOnError = false, ...files: string[]): Promise<void> => { | ||||
|   for (const file of files) { | ||||
|     try { | ||||
|       await fs.unlink(file); | ||||
|       logger.debug(`Deleting file '${file}'`); | ||||
|     } catch (err: any) { | ||||
|       if (err.code === 'ENOENT') { | ||||
|         return; | ||||
|       } | ||||
|       const msg = `Could not delete '${file}'. Inner exception: ${err.message}`; | ||||
|       if (throwsOnError) { | ||||
|         logger.error(msg); | ||||
|         throw err; | ||||
|       } | ||||
|       logger.warn(msg); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const _renameFiles = async ( | ||||
|   logger: Logger, | ||||
|   throwsOnError = false, | ||||
|   ...files: Array<{ src: string; dest: string }> | ||||
| ): Promise<void> => { | ||||
|   for (const file of files) { | ||||
|     try { | ||||
|       await fs.rename(file.src, file.dest); | ||||
|       logger.debug(`Renaming file '${file.src}' to '${file.dest}'`); | ||||
|     } catch (err: any) { | ||||
|       const msg = `Could not rename '${file.src}' to '${file.dest}'. Inner exception: ${err.message}`; | ||||
|       if (throwsOnError) { | ||||
|         logger.error(msg); | ||||
|         err.message = msg; | ||||
|         throw err; | ||||
|       } | ||||
|       logger.warn(msg); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										30
									
								
								apps/ebitemp-api/src/modules/health/health.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								apps/ebitemp-api/src/modules/health/health.controller.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common'; | ||||
| import { | ||||
|   DiskHealthIndicator, | ||||
|   HealthCheck, | ||||
|   HealthCheckService, | ||||
|   MemoryHealthIndicator, | ||||
|   TypeOrmHealthIndicator, | ||||
| } from '@nestjs/terminus'; | ||||
| import { Public } from '../auth/strategies/jwt/jwt-auth.guard'; | ||||
| 
 | ||||
| @Public() | ||||
| @Controller({ path: 'health', version: VERSION_NEUTRAL }) | ||||
| export class HealthController { | ||||
|   constructor( | ||||
|     private readonly health: HealthCheckService, | ||||
|     private readonly db: TypeOrmHealthIndicator, | ||||
|     private readonly disk: DiskHealthIndicator, | ||||
|     private readonly memory: MemoryHealthIndicator | ||||
|   ) {} | ||||
| 
 | ||||
|   @Get() | ||||
|   @HealthCheck() | ||||
|   check() { | ||||
|     return this.health.check([ | ||||
|       () => this.db.pingCheck('database'), | ||||
|       () => this.disk.checkStorage('storage', { path: '/', thresholdPercent: 0.5 }), // Threshold at 50% space occupied in path,
 | ||||
|       () => this.memory.checkHeap('memory', 150 * 1024 * 1024), // Threshold at 150MB memory occupied by process
 | ||||
|     ]); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										11
									
								
								apps/ebitemp-api/src/modules/health/health.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								apps/ebitemp-api/src/modules/health/health.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { TerminusModule } from '@nestjs/terminus'; | ||||
| import { HealthController } from './health.controller'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [TerminusModule.forRoot({ | ||||
|     errorLogStyle: 'pretty', | ||||
|   })], | ||||
|   controllers: [HealthController], | ||||
| }) | ||||
| export class AppHealthModule {} | ||||
							
								
								
									
										17
									
								
								apps/ebitemp-api/src/modules/keyv/keyv.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								apps/ebitemp-api/src/modules/keyv/keyv.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import coerceRecordTypes from '../config/utils/coerce-record-types'; | ||||
| import { registerAs } from '@nestjs/config'; | ||||
| import { z } from 'zod'; | ||||
| 
 | ||||
| export const keyvSchema = z.object({ | ||||
|   redis: z.string(), | ||||
| }); | ||||
| export type KeyvConfig = z.infer<typeof keyvSchema>; | ||||
| 
 | ||||
| export const keyvConfig = registerAs('keyv', () => { | ||||
|   const env = coerceRecordTypes(process.env); | ||||
| 
 | ||||
|   const config: KeyvConfig = keyvSchema.strict().parse({ | ||||
|     redis: env['REDIS_CONNECTION_STRING'], | ||||
|   }); | ||||
|   return config; | ||||
| }); | ||||
							
								
								
									
										11
									
								
								apps/ebitemp-api/src/modules/keyv/keyv.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								apps/ebitemp-api/src/modules/keyv/keyv.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| import { keyvConfig } from './keyv.config'; | ||||
| import { KeyvService } from './keyv.service'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ConfigModule.forFeature(keyvConfig)], | ||||
|   providers: [KeyvService], | ||||
|   exports: [KeyvService], | ||||
| }) | ||||
| export class KeyvModule {} | ||||
							
								
								
									
										16
									
								
								apps/ebitemp-api/src/modules/keyv/keyv.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								apps/ebitemp-api/src/modules/keyv/keyv.service.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| import { createKeyv, KeyvRedisOptions } from '@keyv/redis'; | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { parse, stringify } from 'flatted'; | ||||
| import { keyvConfig, KeyvConfig } from './keyv.config'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class KeyvService { | ||||
|   constructor(@Inject(keyvConfig.KEY) private readonly config: KeyvConfig) {} | ||||
| 
 | ||||
|   create(options?: KeyvRedisOptions) { | ||||
|     const keyv = createKeyv(this.config.redis, options); | ||||
|     keyv.serialize = stringify; | ||||
|     keyv.deserialize = parse; | ||||
|     return keyv; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										21
									
								
								apps/ebitemp-api/src/modules/logger/logger.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								apps/ebitemp-api/src/modules/logger/logger.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| import coerceRecordTypes from '../config/utils/coerce-record-types'; | ||||
| import { registerAs } from '@nestjs/config'; | ||||
| import { z } from 'zod'; | ||||
| 
 | ||||
| export const loggerSchema = z.object({ | ||||
|   seqServerHost: z.string(), | ||||
|   seqServerPort: z.number().finite().positive(), | ||||
|   seqApiKey: z.string(), | ||||
| }); | ||||
| export type LoggerConfig = z.infer<typeof loggerSchema>; | ||||
| 
 | ||||
| export const loggerConfig = registerAs('logger', () => { | ||||
|   const env = coerceRecordTypes(process.env); | ||||
| 
 | ||||
|   const config: LoggerConfig = loggerSchema.strict().parse({ | ||||
|     seqServerHost: env['SEQ_SERVER_HOST'], | ||||
|     seqServerPort: env['SEQ_SERVER_PORT'], | ||||
|     seqApiKey: env['SEQ_API_KEY'], | ||||
|   }); | ||||
|   return config; | ||||
| }); | ||||
							
								
								
									
										59
									
								
								apps/ebitemp-api/src/modules/logger/logger.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								apps/ebitemp-api/src/modules/logger/logger.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { NestApplication } from '@nestjs/core'; | ||||
| import { LoggerModule } from 'nestjs-pino'; | ||||
| import { LoggerConfig, loggerConfig } from './logger.config'; | ||||
| import { ConfigModule } from '@nestjs/config'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     ConfigModule.forFeature(loggerConfig), | ||||
|     LoggerModule.forRootAsync({ | ||||
|       imports: loggerConfig.asProvider().imports, | ||||
|       useFactory: (loggerConfig: LoggerConfig) => ({ | ||||
|         pinoHttp: { | ||||
|           autoLogging: true, | ||||
|           quietReqLogger: true, | ||||
|           quietResLogger: true, | ||||
|           level: 'trace', | ||||
|           useLevel: 'trace', | ||||
|           mixin() { | ||||
|             return { context: NestApplication.name }; | ||||
|           }, | ||||
|           customSuccessMessage: (req, res) => { return `${req.method} ${req.url}: ${res.statusCode} request succedeed` }, | ||||
|           customErrorMessage: (req, res) => { return `${req.method} ${req.url}: ${res.statusCode} request errored` }, | ||||
|           name: 'ebitemp-api', | ||||
|           transport: { | ||||
|             targets: [ | ||||
|               ...(process.env['NODE_ENV'] !== 'production' | ||||
|                 ? [ | ||||
|                     { | ||||
|                       target: 'pino-pretty', | ||||
|                       level: 'debug', | ||||
|                       options: { | ||||
|                         colorize: true, | ||||
|                         messageFormat: '[{context}] {msg}', | ||||
|                         ignore: 'pid,hostname,context', | ||||
|                         translateTime: 'SYS:yyyy-mm-dd HH:MM:ss o', | ||||
|                       }, | ||||
|                     }, | ||||
|                   ] | ||||
|                 : []), | ||||
|               { | ||||
|                 target: '@autotelic/pino-seq-transport', | ||||
|                 level: 'trace', | ||||
|                 options: { | ||||
|                   loggerOpts: { | ||||
|                     serverUrl: `${loggerConfig.seqServerHost}:${loggerConfig.seqServerPort}`, | ||||
|                     apiKey: loggerConfig.seqApiKey, | ||||
|                   }, | ||||
|                 }, | ||||
|               }, | ||||
|             ], | ||||
|           }, | ||||
|         }, | ||||
|       }), | ||||
|       inject: [loggerConfig.KEY], | ||||
|     }), | ||||
|   ], | ||||
| }) | ||||
| export class AppLoggerModule {} | ||||
| @ -0,0 +1,17 @@ | ||||
| import { Global, Module } from '@nestjs/common'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { ApiClientEntity, ApiClientInvocazioniEntity } from '../../database/connections/ebitemp-api'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../../database/connections/ebitemp-api/database.constants'; | ||||
| import { KeyvModule } from '../../keyv/keyv.module'; | ||||
| import { ClientsService } from './clients.service'; | ||||
| 
 | ||||
| @Global() | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     KeyvModule, | ||||
|     TypeOrmModule.forFeature([ApiClientEntity, ApiClientInvocazioniEntity], EBITEMP_API_DATASOURCE), | ||||
|   ], | ||||
|   providers: [ClientsService], | ||||
|   exports: [ClientsService], | ||||
| }) | ||||
| export class ClientsModule {} | ||||
| @ -0,0 +1,86 @@ | ||||
| import { Injectable, Logger } from '@nestjs/common'; | ||||
| import { InjectRepository } from '@nestjs/typeorm'; | ||||
| import { isError } from '@stdlib/assert'; | ||||
| import AsyncLock from 'async-lock'; | ||||
| import { Keyv } from 'cacheable'; | ||||
| import { isObject } from 'lodash'; | ||||
| import { Repository } from 'typeorm'; | ||||
| import { runInTransaction } from 'typeorm-transactional'; | ||||
| import { ApiClientEntity, ApiClientInvocazioniEntity } from '../../database/connections/ebitemp-api'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../../database/connections/ebitemp-api/database.constants'; | ||||
| import { KeyvService } from '../../keyv/keyv.service'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class ClientsService { | ||||
|   private readonly logger = new Logger(ClientsService.name); | ||||
|   private readonly lock = new AsyncLock(); | ||||
| 
 | ||||
|   private readonly keyv: Keyv; | ||||
| 
 | ||||
|   constructor( | ||||
|     keyvService: KeyvService, | ||||
|     @InjectRepository(ApiClientEntity, EBITEMP_API_DATASOURCE) | ||||
|     private readonly clientRepository: Repository<ApiClientEntity>, | ||||
|     @InjectRepository(ApiClientInvocazioniEntity, EBITEMP_API_DATASOURCE) | ||||
|     private readonly clientInvocazioniRepository: Repository<ApiClientInvocazioniEntity> | ||||
|   ) { | ||||
|     this.keyv = keyvService.create({ | ||||
|       namespace: ClientsService.name, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async insertOne( | ||||
|     baseUrl: string, | ||||
|     path: string, | ||||
|     method: string, | ||||
|     requestBody: any, | ||||
|     responseBody: any, | ||||
|     hasFailed: boolean, | ||||
|     errors: string | null | ||||
|   ) { | ||||
|     try { | ||||
|       return await runInTransaction( | ||||
|         async () => { | ||||
|           const client = await this.getClient(baseUrl); | ||||
|           if (!client) return; | ||||
| 
 | ||||
|           const payload = JSON.stringify(requestBody); | ||||
|           const response = isObject(responseBody) ? JSON.stringify(responseBody) : responseBody; | ||||
| 
 | ||||
|           const invocazione = this.clientInvocazioniRepository.create({ | ||||
|             idClient: client.id, | ||||
|             percorso: path, | ||||
|             metodo: method, | ||||
|             timestamp: new Date(), | ||||
|             richiesta: payload, | ||||
|             risposta: response, | ||||
|             errori: errors, | ||||
|             flagErrore: hasFailed, | ||||
|           }); | ||||
| 
 | ||||
|           await this.clientInvocazioniRepository.insert(invocazione); | ||||
|           return invocazione; | ||||
|         }, | ||||
|         { connectionName: EBITEMP_API_DATASOURCE } | ||||
|       ); | ||||
|     } catch (err) { | ||||
|       if (!isError(err)) throw err; | ||||
|       this.logger.error(`Unexpected error: ${err.message}`); | ||||
|       throw err; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async getClient(baseUrl: string) { | ||||
|     const key = `client:baseUrl#${baseUrl}`; | ||||
| 
 | ||||
|     return await this.lock.acquire(key, async () => { | ||||
|       let client = await this.keyv.get<ApiClientEntity>(key); | ||||
|       const isInCache = client != null; | ||||
|       if (!isInCache) { | ||||
|         client ??= (await this.clientRepository.findOne({ where: { baseUrl: baseUrl } })) ?? undefined; | ||||
|         if (client) this.keyv.set(key, client); | ||||
|       } | ||||
|       return client; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| import { Global, Module } from '@nestjs/common'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { ApiEndpointEntity, ApiEndpointInvocazioniEntity } from '../../database/connections/ebitemp-api'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../../database/connections/ebitemp-api/database.constants'; | ||||
| import { KeyvModule } from '../../keyv/keyv.module'; | ||||
| import { EndpointsService } from './endpoints.service'; | ||||
| 
 | ||||
| @Global() | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     KeyvModule, | ||||
|     TypeOrmModule.forFeature([ApiEndpointEntity, ApiEndpointInvocazioniEntity], EBITEMP_API_DATASOURCE), | ||||
|   ], | ||||
|   providers: [EndpointsService], | ||||
|   exports: [EndpointsService], | ||||
| }) | ||||
| export class EndpointsModule {} | ||||
| @ -0,0 +1,84 @@ | ||||
| import { Injectable, Logger } from '@nestjs/common'; | ||||
| import { InjectRepository } from '@nestjs/typeorm'; | ||||
| import AsyncLock from 'async-lock'; | ||||
| import { Keyv } from 'cacheable'; | ||||
| import { Repository } from 'typeorm'; | ||||
| import { runInTransaction } from 'typeorm-transactional'; | ||||
| import { AccountsEntity, ApiEndpointEntity, ApiEndpointInvocazioniEntity } from '../../database/connections/ebitemp-api'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../../database/connections/ebitemp-api/database.constants'; | ||||
| import { KeyvService } from '../../keyv/keyv.service'; | ||||
| import { RequestLog } from '../request-logger.interceptor'; | ||||
| import { isError } from '@stdlib/assert'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class EndpointsService { | ||||
|   private readonly logger = new Logger(EndpointsService.name); | ||||
|   private readonly lock = new AsyncLock(); | ||||
| 
 | ||||
|   private readonly keyv: Keyv; | ||||
| 
 | ||||
|   constructor( | ||||
|     keyvService: KeyvService, | ||||
|     @InjectRepository(ApiEndpointEntity, EBITEMP_API_DATASOURCE) | ||||
|     private readonly endpointRepository: Repository<ApiEndpointEntity>, | ||||
|     @InjectRepository(ApiEndpointInvocazioniEntity, EBITEMP_API_DATASOURCE) | ||||
|     private readonly endpointInvocazioniRepository: Repository<ApiEndpointInvocazioniEntity> | ||||
|   ) { | ||||
|     this.keyv = keyvService.create({ | ||||
|       namespace: EndpointsService.name, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async insertOne( | ||||
|     user: AccountsEntity, | ||||
|     route: string, | ||||
|     method: string, | ||||
|     requestLog: RequestLog, | ||||
|     responseLog: any, | ||||
|     hasFailed: boolean | ||||
|   ) { | ||||
|     try { | ||||
|       return await runInTransaction( | ||||
|         async () => { | ||||
|           const endpoint = await this.getEndpoint(route, method); | ||||
|           if (!endpoint) return; | ||||
| 
 | ||||
|           const payload = JSON.stringify(requestLog); | ||||
|           const response = JSON.stringify(responseLog); | ||||
| 
 | ||||
|           const invocazione = this.endpointInvocazioniRepository.create({ | ||||
|             idAccount: user.id, | ||||
|             idEndpoint: endpoint.id, | ||||
|             timestamp: new Date(), | ||||
|             richiesta: payload, | ||||
|             risposta: response, | ||||
|             flagErrore: hasFailed, | ||||
|           }); | ||||
| 
 | ||||
|           await this.endpointInvocazioniRepository.insert(invocazione); | ||||
|           return invocazione; | ||||
|         }, | ||||
|         { connectionName: EBITEMP_API_DATASOURCE } | ||||
|       ); | ||||
|     } catch (err) { | ||||
|       if (!isError(err)) throw err; | ||||
|       this.logger.error(`Unexpected error: ${err.message}`); | ||||
|       throw err; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async getEndpoint(route: string, method: string) { | ||||
|     const key = `endpoint:route,method#${route},${method}`; | ||||
| 
 | ||||
|     return await this.lock.acquire(key, async () => { | ||||
|       let endpoint = await this.keyv.get<ApiEndpointEntity>(key); | ||||
|       const isInCache = endpoint != null; | ||||
|       if (!isInCache) { | ||||
|         endpoint ??= | ||||
|           (await this.endpointRepository.findOne({ where: { percorso: route, metodo: method } })) ?? undefined; | ||||
|         if (endpoint) this.keyv.set(key, endpoint); | ||||
|       } | ||||
|       return endpoint; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,67 @@ | ||||
| import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common'; | ||||
| import { omit } from 'lodash'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { catchError, concatMap } from 'rxjs/operators'; | ||||
| import { EndpointsService } from './endpoints/endpoints.service'; | ||||
| import { RequestWithUser } from '../auth/constants/request-with-user'; | ||||
| 
 | ||||
| export type RequestLog = { | ||||
|   url: string; | ||||
|   idAccount: string; | ||||
|   params: object; | ||||
|   query: object; | ||||
|   body: object; | ||||
| }; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class RequestLoggerInterceptor implements NestInterceptor { | ||||
|   private readonly logger = new Logger(RequestLoggerInterceptor.name); | ||||
| 
 | ||||
|   constructor(private readonly endpointsService: EndpointsService) {} | ||||
| 
 | ||||
|   async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> { | ||||
|     const request: RequestWithUser = context.switchToHttp().getRequest(); | ||||
| 
 | ||||
|     const user = request.user; | ||||
|     const route = request.routeOptions.url ?? '/'; | ||||
|     const method = request.method as string; | ||||
| 
 | ||||
|     request.params = { ...(request.params as object ?? {}), route, method }; | ||||
| 
 | ||||
|     const requestLog = { | ||||
|       url: request.url, | ||||
|       idAccount: `${user.id}`, | ||||
|       params: omit((request.params as object), ['user', 'route', 'method']), | ||||
|       query: omit((request.query as object), ['__context']), | ||||
|       body: request.body as object, | ||||
|     }; | ||||
| 
 | ||||
|     return next.handle().pipe( | ||||
|       concatMap(async (res: any) => { | ||||
|         await this.endpointsService.insertOne(user, route, method, requestLog, res, false); | ||||
|         return res; | ||||
|       }), | ||||
|       catchError(async (err: any) => { | ||||
|         const getExceptionResponse = (exception: any): any => { | ||||
|           if (typeof exception.response === 'object') { | ||||
|             const { error, message, ...rest } = exception.response; | ||||
|             return { | ||||
|               error, | ||||
|               message, | ||||
|               ...rest, | ||||
|             }; | ||||
|           } | ||||
|           return { | ||||
|             error: exception.name, | ||||
|             message: exception.message, | ||||
|           }; | ||||
|         }; | ||||
| 
 | ||||
|         const res = getExceptionResponse(err); | ||||
| 
 | ||||
|         await this.endpointsService.insertOne(user, route, method, requestLog, res, true); | ||||
|         throw err; | ||||
|       }) | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| import { Global, Module } from '@nestjs/common'; | ||||
| import { APP_INTERCEPTOR } from '@nestjs/core'; | ||||
| import { ClientsModule } from './clients/clients.module'; | ||||
| import { EndpointsModule } from './endpoints/endpoints.module'; | ||||
| import { RequestLoggerInterceptor } from './request-logger.interceptor'; | ||||
| 
 | ||||
| @Global() | ||||
| @Module({ | ||||
|   imports: [EndpointsModule, ClientsModule], | ||||
|   providers: [], | ||||
|   exports: [EndpointsModule, ClientsModule], | ||||
| }) | ||||
| export class AppRequestLoggingModule {} | ||||
							
								
								
									
										13
									
								
								apps/ebitemp-api/src/modules/schedule/schedule.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								apps/ebitemp-api/src/modules/schedule/schedule.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { ScheduleModule } from '@nestjs/schedule'; | ||||
| import { TypeOrmModule } from '@nestjs/typeorm'; | ||||
| import { TipiJobsEntity } from '../database/connections/ebitemp-api/entities'; | ||||
| import { SchedulerService } from './schedule.service'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../database/connections/ebitemp-api/database.constants'; | ||||
| 
 | ||||
| @Module({ | ||||
|   imports: [TypeOrmModule.forFeature([TipiJobsEntity], EBITEMP_API_DATASOURCE), ScheduleModule.forRoot()], | ||||
|   providers: [SchedulerService], | ||||
|   exports: [], | ||||
| }) | ||||
| export class AppScheduleModule {} | ||||
							
								
								
									
										56
									
								
								apps/ebitemp-api/src/modules/schedule/schedule.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								apps/ebitemp-api/src/modules/schedule/schedule.service.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| import { Injectable, Logger } from '@nestjs/common'; | ||||
| import { SchedulerRegistry } from '@nestjs/schedule'; | ||||
| import { InjectRepository } from '@nestjs/typeorm'; | ||||
| import { isError } from '@stdlib/assert'; | ||||
| import { CronJob, CronTime } from 'cron'; | ||||
| import { Repository } from 'typeorm'; | ||||
| import { EBITEMP_API_DATASOURCE } from '../database/connections/ebitemp-api/database.constants'; | ||||
| import { TipiJobsEntity } from '../database/connections/ebitemp-api/entities/tipi_jobs.entity'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class SchedulerService { | ||||
|   private readonly logger = new Logger(SchedulerService.name); | ||||
| 
 | ||||
|   private static readonly TIME_ZONE = 'Europe/Rome'; | ||||
| 
 | ||||
|   constructor( | ||||
|     private readonly schedulerRegistry: SchedulerRegistry, | ||||
|     @InjectRepository(TipiJobsEntity, EBITEMP_API_DATASOURCE) | ||||
|     private readonly tipiJobsRepository: Repository<TipiJobsEntity> | ||||
|   ) {} | ||||
| 
 | ||||
|   async onApplicationBootstrap() { | ||||
|     try { | ||||
|       const jobs = this.schedulerRegistry.getCronJobs(); | ||||
|       const jobTypes = await this.tipiJobsRepository.find(); | ||||
| 
 | ||||
|       for (const [jobName, job] of jobs) { | ||||
|         this.rescheduleJobAccordingToJobType(jobTypes, jobName, job); | ||||
|       } | ||||
|     } catch (err) { | ||||
|       if (!isError(err)) throw err; | ||||
|       this.logger.error(err.message); | ||||
|       this.logger.warn(`Error while retrieving scheduled jobs on database. Skipping jobs.`); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private rescheduleJobAccordingToJobType(jobTypes: TipiJobsEntity[], jobName: string, job: CronJob) { | ||||
|     const jobType = jobTypes.find((jobType) => jobType.nome == jobName); | ||||
| 
 | ||||
|     if (!jobType) { | ||||
|       this.logger.warn(`Job type for job '${jobName}' not found on database. Skipping job.`); | ||||
|       this.schedulerRegistry.deleteCronJob(jobName); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const jobTime = new CronTime(jobType.patternCron, SchedulerService.TIME_ZONE); | ||||
|     job.setTime(jobTime); | ||||
|     job.start(); | ||||
| 
 | ||||
|     this.logger.log( | ||||
|       `Job type id '${jobType.id}' found for job '${jobName}' [isActive: ${jobType.flagAttivo}, cronPattern: ${ | ||||
|         jobType.patternCron | ||||
|       }]. Upcoming date => ${job.nextDate()}` | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,4 @@ | ||||
| 
 | ||||
| export async function patchNestjsSwagger() { | ||||
|   (await import('@anatine/zod-nestjs')).patchNestjsSwagger(); | ||||
| }; | ||||
							
								
								
									
										12
									
								
								apps/ebitemp-api/src/modules/validation/validation.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								apps/ebitemp-api/src/modules/validation/validation.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; | ||||
| import { ZodValidationPipe } from './zod.pipe'; | ||||
| import { ZodSerializerInterceptor } from './zod.serializer'; | ||||
| 
 | ||||
| @Module({ | ||||
|   providers: [ | ||||
|     { provide: APP_PIPE, useClass: ZodValidationPipe }, | ||||
|     { provide: APP_INTERCEPTOR, useClass: ZodSerializerInterceptor }, | ||||
|   ], | ||||
| }) | ||||
| export class AppValidationModule {} | ||||
							
								
								
									
										36
									
								
								apps/ebitemp-api/src/modules/validation/zod.pipe.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								apps/ebitemp-api/src/modules/validation/zod.pipe.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| import { ZodDtoStatic } from '@anatine/zod-nestjs'; | ||||
| import { HTTP_ERRORS_BY_CODE } from '@anatine/zod-nestjs/src/lib/http-errors'; | ||||
| 
 | ||||
| import { ArgumentMetadata, HttpStatus, Injectable, Optional, PipeTransform } from '@nestjs/common'; | ||||
| 
 | ||||
| export interface ZodValidationPipeOptions { | ||||
|   errorHttpStatusCode?: keyof typeof HTTP_ERRORS_BY_CODE; | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| export class ZodValidationPipe implements PipeTransform { | ||||
|   private readonly errorHttpStatusCode: keyof typeof HTTP_ERRORS_BY_CODE; | ||||
| 
 | ||||
|   constructor(@Optional() options?: ZodValidationPipeOptions) { | ||||
|     this.errorHttpStatusCode = options?.errorHttpStatusCode || HttpStatus.BAD_REQUEST; | ||||
|   } | ||||
| 
 | ||||
|   public transform(value: unknown, metadata: ArgumentMetadata): unknown { | ||||
|     const zodSchema = (metadata?.metatype as ZodDtoStatic)?.zodSchema; | ||||
| 
 | ||||
|     if (zodSchema) { | ||||
|       const parseResult = zodSchema.safeParse(value); | ||||
| 
 | ||||
|       if (!parseResult.success) { | ||||
|         const { error } = parseResult; | ||||
|         const message = error.errors.map((error) => `${error.path.join('.')}: ${error.message}`); | ||||
| 
 | ||||
|         throw new HTTP_ERRORS_BY_CODE[this.errorHttpStatusCode](message); | ||||
|       } | ||||
| 
 | ||||
|       return parseResult.data; | ||||
|     } | ||||
| 
 | ||||
|     return value; | ||||
|   } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user