Template de archivos, productor - consumidor y lanzamiento paralelo de

servicios
This commit is contained in:
2026-01-12 13:08:56 +01:00
parent 8a1368d42a
commit 707467fc6f
30 changed files with 459 additions and 215 deletions

22
packages/.env Normal file
View File

@@ -0,0 +1,22 @@
PORT=3000
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
ENVIORMENT=development
#RABBITMQ_HOST=rabbitmq-sim-broker
RABBITMQ_HOST=localhost
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_SECURE=false
RABBITMQ_VHOST=sim-vhost
# Hay cosas que unificar de varios servicios
POSTGRES_DB=postgres
POSTGRES_DATABASE=postres
POSTGRES_HOST=postgresql-sim-1
POSTGRES_PORT=5432
DEV_POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=1234

18
packages/_template/config/env/index.ts vendored Normal file
View File

@@ -0,0 +1,18 @@
export const env = {
ENVIRONMENT: process.env.ENVIORMENT,
POSTGRES_USER: process.env.POSTGRES_USER,
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
POSTGRES_PORT: process.env.POSTGRES_PORT,
POSTGRES_HOST: process.env.POSTGRES_HOST,
POSTGRES_DATABASE: process.env.POSTGRES_DATABASE,
RABBITMQ_HOST: String(process.env.RABBITMQ_HOST ?? "localhost"),
RABBITMQ_USER: String(process.env.RABBITMQ_USER ?? "guest"),
RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD ?? "guest"),
RABBITMQ_EXCHANGE: String(process.env.RABBITMQ_EXCHANGE ?? "/"),
RABBITMQ_PORT: parseInt(process.env.RABBITMQ_PORT ?? "5672"),
RABBITMQ_MODULENAME: process.env.MODULENAME,
RABBITMQ_TTL: process.env.RABBITMQ_TTL,
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST ?? "/"),
};

View File

@@ -0,0 +1,3 @@
console.log("Template")
export default {}

View File

@@ -0,0 +1,32 @@
{
"name": "sim-template",
"version": "1.0.0",
"description": "Template de la estructura de archivos",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx index.ts"
},
"author": "",
"license": "ISC",
"packageManager": "yarn@4.12.0",
"dependencies": {
"@tsconfig/node22": "*",
"amqplib": "^0.10.9",
"cors": "*",
"dotenv": "*",
"express": "*",
"typescript": "*"
},
"devDependencies": {
"@types/amqplib": "^0.10.8",
"@types/cors": "*",
"@types/express": "*",
"@types/node": "*",
"@types/supertest": "*",
"prettier": "*",
"supertest": "*",
"tsx": "*",
"vitest": "*"
}
}

View File

@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/shared",
"outDir": "../../dist/sim-gestor-eventos",
"baseUrl": ".",
"paths": {
"#adapters/*": [
@@ -17,8 +17,8 @@
"__tests__/*"
],
"#shared/*": [
"./*"
]
"../shared/*"
],
}
},
"exclude": [

View File

@@ -0,0 +1,78 @@
import client, { ChannelModel, ConfirmChannel, connect, Connection } from "amqplib"
const PREFETCH_LIMIT = 1
export class RabbitConnection {
connection?: ChannelModel
channel?: ConfirmChannel
connected: Boolean = false
private connectionOptions: {
hostname: string,
port: number,
secure: boolean,
username: string,
password: string,
vhost: string
}
constructor(opts: typeof this.connectionOptions) {
this.connectionOptions = opts
this.checkStructure();
}
/**
* Verificacion que la estructura definida en el JSON corresponde con
* la esperada
* TODO: Faltan las colas fijas según las operaciones
*/
private checkStructure() {
this.channel?.assertQueue("sim.queue")
this.channel?.assertExchange("sim.exchange", "direct")
}
public async connect() {
this.connection = await this.createConnection();
if (this.connection == undefined) throw new Error("[RMQ] Error crecreando la conexion")
this.channel = await this.createConfirmChannel()
}
protected async createConnection() {
const { hostname, port, secure } = { ...this.connectionOptions }
const { username, password } = { ...this.connectionOptions };
const protocol = secure ? 'amqps' : 'amqp';
const vhost = this.connectionOptions.vhost
const connection = await client.connect({
protocol,
hostname,
port,
username,
password,
vhost
});
connection.on('error', (error: unknown) => {
console.error(`[RMQ] Rabbitmq connection error :: ${error}`);
Promise.reject(error);
});
return connection;
}
protected async createConfirmChannel() {
const channel = await this.connection?.createConfirmChannel()
await channel?.prefetch(PREFETCH_LIMIT)
if (channel == undefined) throw new Error("[RMQ] Error crecreando el canal")
channel.on('error', (error: unknown) => {
console.error(`[RMQ] Rabbitmq channel error :: ${error}`);
Promise.reject(error);
});
return channel;
}
}

View File

@@ -1,45 +0,0 @@
import { bool, cleanEnv, num, port, str } from 'envalid';
import { USER_ROLES } from '#shared/domain/models/authorizer-user.entity';
import { ensureEnvString } from './env-validators';
export const env = cleanEnv(process.env, {
ENVIRONMENT: str({
choices: ['local', 'development', 'production']
}),
USE_IN_MEMORY_REPOSITORIES: bool(),
ALERTS_API_PORT: port(),
COMPANIES_COMMAND_URL: ensureEnvString(),
DEVICES_COMMAND_URL: ensureEnvString(),
USERS_COMMAND_URL: ensureEnvString(),
REDIS_PASSWORD: ensureEnvString(),
REDIS_PORT: port(),
REDIS_HOST: ensureEnvString(),
POSTGRES_USER: ensureEnvString(),
POSTGRES_PASSWORD: ensureEnvString(),
POSTGRES_PORT: port(),
POSTGRES_HOST: ensureEnvString(),
POSTGRES_DATABASE: ensureEnvString(),
RABBITMQ_HOST: ensureEnvString(),
RABBITMQ_USER: ensureEnvString(),
RABBITMQ_PASSWORD: ensureEnvString(),
RABBITMQ_EXCHANGE: ensureEnvString(),
RABBITMQ_PORT: port(),
RABBITMQ_MODULENAME: ensureEnvString(),
RABBITMQ_TTL: port(),
RABBITMQ_SECURE: bool(),
RABBITMQ_RETRY_INTERVAL: num(),
SYSTEM_USER_ID: ensureEnvString(),
SYSTEM_USER_EMAIL: ensureEnvString(),
SYSTEM_USER_ROLE: str({
choices: [
USER_ROLES.ADMIN,
USER_ROLES.CLIENT,
USER_ROLES.DELEGATION_MANAGER,
USER_ROLES.OPERATOR,
USER_ROLES.ROOM_STAFF
],
default: USER_ROLES.ADMIN
})
});

View File

@@ -1,12 +0,0 @@
import { env } from '#api/config/env';
export const eventBusConfig = {
host: env.RABBITMQ_HOST,
user: env.RABBITMQ_USER,
password: env.RABBITMQ_PASSWORD,
exchange: env.RABBITMQ_EXCHANGE,
port: env.RABBITMQ_PORT,
modulename: env.RABBITMQ_MODULENAME,
ttl: env.RABBITMQ_TTL,
secure: env.RABBITMQ_SECURE
};

View File

@@ -1,2 +0,0 @@
console.log("shared")
export default {}

View File

@@ -2,23 +2,23 @@
"name": "sim-shared",
"version": "1.0.0",
"description": "",
"main": "src/app.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx index.ts"
"dev": "echo \" Shared no es un modulo ejecutable \" "
},
"author": "",
"license": "ISC",
"private": true,
"packageManager": "yarn@4.12.0",
"dependencies": {
"@tsconfig/node22": "*",
"amqplib": "^0.10.9",
"cors": "*",
"dotenv": "*",
"express": "*",
"typescript": "*"
},
"devDependencies": {
"@types/amqplib": "^0.10.8",
"@types/cors": "*",
"@types/express": "*",
"@types/node": "*",

View File

@@ -0,0 +1,7 @@
export interface AMQPclient {
connect: () => Promise<void>;
disconnect: () => Promise<void>;
sendToQueue: (queue: string, msg: string, callback: () => {}) => boolean,
}

View File

@@ -0,0 +1,22 @@
PORT=3000
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
ENVIORMENT=development
#RABBITMQ_HOST=rabbitmq-sim-broker
RABBITMQ_HOST=localhost
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_SECURE=false
RABBITMQ_VHOST=sim-vhost
# Hay cosas que unificar de varios servicios
POSTGRES_DB=postgres
POSTGRES_DATABASE=postres
POSTGRES_HOST=postgresql-sim-1
POSTGRES_PORT=5432
DEV_POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=1234

View File

@@ -0,0 +1,24 @@
import { loadEnvFile } from "node:process";
loadEnvFile("../../.env")
export const env = {
ENVIRONMENT: process.env.ENVIORMENT,
POSTGRES_USER: process.env.POSTGRES_USER,
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
POSTGRES_PORT: process.env.POSTGRES_PORT,
POSTGRES_HOST: process.env.POSTGRES_HOST,
POSTGRES_DATABASE: process.env.POSTGRES_DATABASE,
RABBITMQ_HOST: String(process.env.RABBITMQ_HOST ?? "localhost"),
RABBITMQ_USER: String(process.env.RABBITMQ_USER ?? "guest"),
RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD ?? "guest"),
RABBITMQ_EXCHANGE: String(process.env.RABBITMQ_EXCHANGE ?? "/"),
RABBITMQ_PORT: parseInt(process.env.RABBITMQ_PORT ?? "5672"),
RABBITMQ_MODULENAME: process.env.MODULENAME,
RABBITMQ_TTL: process.env.RABBITMQ_TTL,
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
};
console.log("Consumidor", env)

View File

@@ -0,0 +1,36 @@
import { env } from "./config/env"
import { RabbitConnection } from "#shared/adapters/queues/RabbitMQClient"
import { ConsumeMessage } from "amqplib"
const rmqUser = env.RABBITMQ_USER
const rmqPass = env.RABBITMQ_PASSWORD
const rmqHost = env.RABBITMQ_HOST
const rmqPort = Number(env.RABBITMQ_PORT)
const rmqSecure = false
const rmqVhost = env.RABBITMQ_VHOST
async function test() {
console.log("iniciado Consumidor", env)
const rbmq = new RabbitConnection({
username: rmqUser,
password: rmqPass,
vhost: String(rmqVhost),
hostname: rmqHost,
port: rmqPort,
secure: rmqSecure
})
await rbmq.connect()
console.log("[Consumidor] iniciado")
await rbmq.channel?.consume("sim.queue", (buff: ConsumeMessage | null) => {
const decoded = buff?.content.toString()
console.log(" [Consumidor] Mensaje recibido ", decoded)
})
}
test()
export default {}

View File

@@ -0,0 +1,32 @@
{
"name": "sim-consumidor",
"version": "1.0.0",
"description": "consumidor generico de envetos de RMQ",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx watch index.ts "
},
"author": "",
"license": "ISC",
"packageManager": "yarn@4.12.0",
"dependencies": {
"@tsconfig/node22": "*",
"amqplib": "^0.10.9",
"cors": "*",
"dotenv": "*",
"express": "*",
"typescript": "*"
},
"devDependencies": {
"@types/amqplib": "^0.10.8",
"@types/cors": "*",
"@types/express": "*",
"@types/node": "*",
"@types/supertest": "*",
"prettier": "*",
"supertest": "*",
"tsx": "*",
"vitest": "*"
}
}

View File

@@ -0,0 +1,37 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/sim-consumidor",
"baseUrl": ".",
"paths": {
"#config/*": [
"config/*"
],
"#adapters/*": [
"adapters/*"
],
"#domain/*": [
"domain/*"
],
"#ports/*": [
"ports/*"
],
"#tests/*": [
"__tests__/*"
],
"#shared/*": [
"../shared/*"
],
}
},
"exclude": [
"node_modules"
],
"include": [
"**/*.ts",
"src/**/*.d.ts"
],
"files": [
"index.ts"
]
}

View File

@@ -1,7 +0,0 @@
import { makeValidator } from 'envalid';
import { isValidEnvString, isValidEnvStringArray } from '#shared/domain/utils/env-validators';
export const ensureEnvStringArray = makeValidator((value: unknown) => isValidEnvStringArray(value));
export const ensureEnvString = makeValidator((value: unknown) => isValidEnvString(value));

View File

@@ -1,3 +1,6 @@
import { loadEnvFile } from "node:process";
loadEnvFile("../../.env")
export const env = {
ENVIRONMENT: process.env.ENVIORMENT,
POSTGRES_USER: process.env.POSTGRES_USER,
@@ -5,14 +8,14 @@ export const env = {
POSTGRES_PORT: process.env.POSTGRES_PORT,
POSTGRES_HOST: process.env.POSTGRES_HOST,
POSTGRES_DATABASE: process.env.POSTGRES_DATABASE,
RABBITMQ_HOST: process.env.RABBITMQ_HOST,
RABBITMQ_USER: process.env.RABBITMQ_USER,
RABBITMQ_PASSWORD: process.env.RABBITMQ_PASSWORD,
RABBITMQ_EXCHANGE: process.env.RABBITMQ_EXCHANGE,
RABBITMQ_PORT: process.env.RABBITMQ_PORT,
RABBITMQ_HOST: String(process.env.RABBITMQ_HOST ?? "localhost"),
RABBITMQ_USER: String(process.env.RABBITMQ_USER ?? "guest"),
RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD ?? "guest"),
RABBITMQ_EXCHANGE: String(process.env.RABBITMQ_EXCHANGE ?? "/"),
RABBITMQ_PORT: parseInt(process.env.RABBITMQ_PORT ?? "5672"),
RABBITMQ_MODULENAME: process.env.MODULENAME,
RABBITMQ_TTL: process.env.RABBITMQ_TTL,
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
RABBITMQ_VHOST: process.env.RABBITMQ_VHOST,
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
};

View File

@@ -1,12 +0,0 @@
import { env } from '#api/config/env';
export const eventBusConfig = {
host: env.RABBITMQ_HOST,
user: env.RABBITMQ_USER,
password: env.RABBITMQ_PASSWORD,
exchange: env.RABBITMQ_EXCHANGE,
port: env.RABBITMQ_PORT,
modulename: env.RABBITMQ_MODULENAME,
ttl: env.RABBITMQ_TTL,
secure: env.RABBITMQ_SECURE
};

View File

@@ -1,5 +1,5 @@
import client, { ChannelModel, ConfirmChannel, connect, Connection } from "amqplib"
import { env } from "config/env"
import { env } from "#config/env"
import { RabbitConnection } from "#shared/adapters/queues/RabbitMQClient"
const rmqUser = env.RABBITMQ_USER
const rmqPass = env.RABBITMQ_PASSWORD
@@ -9,63 +9,22 @@ const rmqSecure = false
const rmqVhost = env.RABBITMQ_VHOST
const PREFETCH_LIMIT = 1
class RabbitConnection {
connection?: ChannelModel
channel?: ConfirmChannel
connected: Boolean = false
public async connect() {
this.connection = await this.createConnection();
if (this.connection == undefined) throw new Error("[RMQ] Error crecreando la conexion")
this.channel = await this.createConfirmChannel()
}
protected async createConnection() {
const { hostname, port, secure } = { hostname: rmqHost, port: rmqPort, secure: rmqSecure }
const { username, password } = { username: rmqUser, password: rmqPass };
const protocol = secure ? 'amqps' : 'amqp';
const vhost = rmqVhost
const connection = await client.connect({
protocol,
hostname,
port,
username,
password,
vhost
});
connection.on('error', (error: unknown) => {
console.error(`[RMQ] Rabbitmq connection error :: ${error}`);
Promise.reject(error);
});
return connection;
}
protected async createConfirmChannel() {
const channel = await this.connection?.createConfirmChannel()
await channel?.prefetch(PREFETCH_LIMIT)
if (channel == undefined) throw new Error("[RMQ] Error crecreando el canal")
channel.on('error', (error: unknown) => {
console.error(`[RMQ] Rabbitmq channel error :: ${error}`);
Promise.reject(error);
});
return channel;
}
}
console.log("[Generador] Iniciando")
async function test() {
const rbmq = new RabbitConnection()
const rbmq = new RabbitConnection({
username: rmqUser,
password: rmqPass,
vhost: String(rmqVhost),
hostname: rmqHost,
port: rmqPort,
secure: rmqSecure
})
await rbmq.connect()
console.log("enviando")
console.log("[Generador] enviando --")
rbmq.channel?.sendToQueue("sim.queue", Buffer.from("test"), {},
function (err, ok) {

View File

@@ -5,7 +5,7 @@
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx index.ts"
"dev": "tsx watch index.ts"
},
"author": "",
"license": "ISC",

View File

@@ -4,14 +4,17 @@
"outDir": "../../dist/sim-gestor-eventos",
"baseUrl": ".",
"paths": {
"#config/*": [
"config/*"
],
"#adapters/*": [
"src/adapters/*"
"adapters/*"
],
"#domain/*": [
"src/domain/*"
"domain/*"
],
"#ports/*": [
"src/ports/*"
"ports/*"
],
"#tests/*": [
"__tests__/*"