Compare commits
19 Commits
1.4.0
...
WEBINT-175
| Author | SHA1 | Date | |
|---|---|---|---|
| 30f1819a4e | |||
| 964ea6add9 | |||
| 602878acf4 | |||
| 0aa52feaac | |||
| 15b70309da | |||
| 7001fccbf7 | |||
| cffee785b2 | |||
| 33d260310c | |||
| e359acc1d5 | |||
| bb4bce4a6d | |||
| eac74ef0cd | |||
| 1dc4eb5648 | |||
| a35a6c2b60 | |||
| 1f78f4a3e1 | |||
| 1e98559f3a | |||
| ef0f860b9d | |||
| 0bff55379f | |||
| 4d34308a13 | |||
| cbbc0f6edb |
3
.env
3
.env
@@ -27,3 +27,6 @@ PGHOST=localhost
|
|||||||
PGUSER=alvar
|
PGUSER=alvar
|
||||||
PGPASSWORD=alvar
|
PGPASSWORD=alvar
|
||||||
PGPORT=5433
|
PGPORT=5433
|
||||||
|
|
||||||
|
# Proxy
|
||||||
|
CONNECTIONS_URL=https://sim-connections.savefamilygps.net
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8933201125068890694
|
iccid: 8933201125065160380
|
||||||
offer: SAVEFAMILY1
|
offer: SAVEFAMILY1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8933201125068886692
|
iccid: 8933201125068890074
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ params:query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8933201125068886692
|
iccid: 8933201125068886700
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
21
docs/sim-api/ReActivate.bru
Normal file
21
docs/sim-api/ReActivate.bru
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
meta {
|
||||||
|
name: ReActivate
|
||||||
|
type: http
|
||||||
|
seq: 13
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{baseurl}}/sim/reActivate
|
||||||
|
body: formUrlEncoded
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:form-urlencoded {
|
||||||
|
iccid: 8933201125065160380
|
||||||
|
~offer: SAVEFAMILY1
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
4
docs/sim-api/environments/simconnections.bru
Normal file
4
docs/sim-api/environments/simconnections.bru
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
vars {
|
||||||
|
baseurl: http://sim-connections.savefamilygps.net
|
||||||
|
}
|
||||||
|
color: #C77A0F
|
||||||
20
docs/sim-api/test proxy.bru
Normal file
20
docs/sim-api/test proxy.bru
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
meta {
|
||||||
|
name: test proxy
|
||||||
|
type: http
|
||||||
|
seq: 14
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{baseurl}}/simconnections/alai/select?iccid=1111111111111111111
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
iccid: 1111111111111111111
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
@@ -5,13 +5,13 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{actionsUrl}}/massActions?massActionId=5192767
|
url: {{actionsUrl}}/massActions?massActionId=5363116
|
||||||
body: formUrlEncoded
|
body: formUrlEncoded
|
||||||
auth: bearer
|
auth: bearer
|
||||||
}
|
}
|
||||||
|
|
||||||
params:query {
|
params:query {
|
||||||
massActionId: 5192767
|
massActionId: 5363116
|
||||||
~identifier.identifierType: ICCID
|
~identifier.identifierType: ICCID
|
||||||
~identifier.identifiers: 8933201125065160463,8933201125065160422
|
~identifier.identifiers: 8933201125065160463,8933201125065160422
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
PORT=3000
|
NOS_BASE_URL=localhost
|
||||||
RABBITMQ_USER=guest
|
|
||||||
RABBITMQ_PASSWORD=guest
|
|
||||||
|
|
||||||
ENVIORMENT=development
|
ENVIORMENT=development
|
||||||
|
|||||||
16
packages/sim-consumidor-nos/config/env/index.ts
vendored
16
packages/sim-consumidor-nos/config/env/index.ts
vendored
@@ -1,5 +1,16 @@
|
|||||||
import { loadEnvFile } from "node:process";
|
import { loadEnvFile } from "node:process";
|
||||||
loadEnvFile("../../.env")
|
import path from "node:path";
|
||||||
|
|
||||||
|
try {
|
||||||
|
loadEnvFile(path.join("./.env")) // base
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error cargando el .env desde ./.env")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
loadEnvFile(path.join("../../.env")) // Global
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error cargando el .env desde ../../.env")
|
||||||
|
}
|
||||||
|
|
||||||
export const env = {
|
export const env = {
|
||||||
ENVIRONMENT: process.env.ENVIORMENT,
|
ENVIRONMENT: process.env.ENVIORMENT,
|
||||||
@@ -18,5 +29,8 @@ export const env = {
|
|||||||
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
||||||
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
||||||
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
||||||
|
|
||||||
|
// ESPECIFICO NOS
|
||||||
|
NOS_BASE_URL: String(process.env.NOS_BASE_URL)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
69
packages/sim-consumidor-nos/config/eventBus.config.ts
Normal file
69
packages/sim-consumidor-nos/config/eventBus.config.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js"
|
||||||
|
import { Channel } from "amqp-connection-manager"
|
||||||
|
import { env } from "./env/index.js"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
export const rmqConnOptions = <RMQConnectionParams>{
|
||||||
|
username: rmqUser,
|
||||||
|
password: rmqPass,
|
||||||
|
vhost: rmqVhost,
|
||||||
|
hostname: rmqHost,
|
||||||
|
port: rmqPort,
|
||||||
|
secure: rmqSecure,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rabbitmqEventBus = new RabbitMQEventBus({
|
||||||
|
connectionParams: rmqConnOptions,
|
||||||
|
buildStructure: buildQueues,
|
||||||
|
maxRetry: 5
|
||||||
|
})
|
||||||
|
|
||||||
|
async function buildQueues(channel: Channel) {
|
||||||
|
const QUEUES = {
|
||||||
|
NOS: "sim.nos",
|
||||||
|
NOSDLX: "sim.nos.dlx",
|
||||||
|
NOSDEL: "sim.nos.delayed",
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXCHANGES = {
|
||||||
|
MAIN: "sim.exchange",
|
||||||
|
DLX: "sim.ex.nos.dlx",
|
||||||
|
DEL: "sim.ex.nos.delayed"
|
||||||
|
}
|
||||||
|
|
||||||
|
const DELAY = 10 * 1000
|
||||||
|
const BASE_NOS_KEY = "sim.nos.#"
|
||||||
|
|
||||||
|
await channel.assertExchange(EXCHANGES.DEL, "topic")
|
||||||
|
await channel.assertExchange(EXCHANGES.DLX, "topic")
|
||||||
|
await channel.assertExchange(EXCHANGES.MAIN, "topic")
|
||||||
|
|
||||||
|
await channel.assertQueue(QUEUES.NOS)
|
||||||
|
await channel.assertQueue(QUEUES.NOSDLX)
|
||||||
|
await channel.assertQueue(QUEUES.NOSDEL, {
|
||||||
|
durable: true,
|
||||||
|
arguments: {
|
||||||
|
'x-message-ttl': DELAY,
|
||||||
|
'x-dead-letter-exchange': EXCHANGES.MAIN,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Cola dead-letter
|
||||||
|
await channel.bindQueue(QUEUES.NOSDLX, EXCHANGES.DLX, "sim.nos.#")
|
||||||
|
// Cola delay
|
||||||
|
await channel.bindQueue(QUEUES.NOSDEL, EXCHANGES.DEL, BASE_NOS_KEY)
|
||||||
|
// Cola nos -> main exchange
|
||||||
|
await channel.bindQueue(QUEUES.NOS, EXCHANGES.MAIN, BASE_NOS_KEY)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startRMQClient() {
|
||||||
|
await rabbitmqEventBus.connect()
|
||||||
|
return rabbitmqEventBus
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { startRMQClient } from "#config/eventBusConfig"
|
import { startRMQClient } from "#config/eventBus.config.js"
|
||||||
import { SimNosController } from "./aplication/SimNOS.controller.js"
|
import { SimNosController } from "./aplication/SimNOS.controller.js"
|
||||||
|
|
||||||
async function startWorker() {
|
async function startWorker() {
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp package.json ../../dist/packages/sim-consumidor-nos/",
|
"build": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp package.json ../../dist/packages/sim-consumidor-nos/",
|
||||||
"esbuild": "esbuild index.ts --platform=node",
|
"esbuild": "esbuild index.ts --platform=node",
|
||||||
"start": "node ../../dist/packages/sim-consumidor-nos/index.js"
|
"start": "node ../../dist/packages/sim-consumidor-nos/index.js",
|
||||||
|
"dev": "tsx watch index.ts"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class SimRouter {
|
|||||||
["activate", this.simController.activate()],
|
["activate", this.simController.activate()],
|
||||||
["pause", this.simController.suspend()],
|
["pause", this.simController.suspend()],
|
||||||
["cancel", this.simController.terminate()],
|
["cancel", this.simController.terminate()],
|
||||||
["reActivate", this.simController.reActivate()],
|
["reactivate", this.simController.reActivate()],
|
||||||
["preActivate", this.simController.preActivate()]
|
["preActivate", this.simController.preActivate()]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,13 +75,14 @@ export class SimUseCases {
|
|||||||
operation: args.operation,
|
operation: args.operation,
|
||||||
iccids: String(args.iccid),
|
iccids: String(args.iccid),
|
||||||
status: "noMassID",
|
status: "noMassID",
|
||||||
request_id: response.data.requestId
|
request_id: response.data.requestId,
|
||||||
|
correlation_id: args.correlation_id
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Esto tiene poco sentido si la operacion ya se
|
// TODO: Esto tiene poco sentido si la operacion ya se
|
||||||
// tenia que haber creado en el generador
|
// tenia que haber creado en el generador
|
||||||
this.logOperation(operation)
|
this.logOperation(operation)
|
||||||
.then().catch(e => console.error(e))
|
.then().catch(e => console.error("Error login operation", e))
|
||||||
|
|
||||||
if (args.correlation_id != undefined) {
|
if (args.correlation_id != undefined) {
|
||||||
this.orderRepository.updateOrder({
|
this.orderRepository.updateOrder({
|
||||||
@@ -115,6 +116,15 @@ export class SimUseCases {
|
|||||||
public activate(activationData: ActivationData): () => Promise<Result<string, boolean>> {
|
public activate(activationData: ActivationData): () => Promise<Result<string, boolean>> {
|
||||||
const OPERATION_URL = "/actions/activateLine"
|
const OPERATION_URL = "/actions/activateLine"
|
||||||
return async () => {
|
return async () => {
|
||||||
|
const iccid = activationData.identifier.identifiers
|
||||||
|
// Comporbación excepcional para saber si la linea está suspendida
|
||||||
|
const statusLinea = await this.objeniousRepository.getLinesAPI("ICCID", [String(iccid)])
|
||||||
|
console.log("statusLinea, ", iccid, statusLinea)
|
||||||
|
if (statusLinea.data != undefined && statusLinea.data[0].status.networkStatus == "SUSPENDED") {
|
||||||
|
const res = await this.reActivate(activationData)()
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
const req = this.httpClient.client.post(OPERATION_URL, {
|
const req = this.httpClient.client.post(OPERATION_URL, {
|
||||||
dueDate: activationData.dueDate,
|
dueDate: activationData.dueDate,
|
||||||
identifier: activationData.identifier,
|
identifier: activationData.identifier,
|
||||||
@@ -198,16 +208,29 @@ export class SimUseCases {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public reActivate(pauseData: ActionData): () => Promise<Result<string, boolean>> {
|
public reActivate(reactivateData: ActionData): () => Promise<Result<string, boolean>> {
|
||||||
const OPERATION_URL = "/actions/reactivateLine"
|
const OPERATION_URL = "/actions/reactivateLine"
|
||||||
return async () => {
|
return async () => {
|
||||||
const req = this.httpClient.client.post(OPERATION_URL, {
|
const req = this.httpClient.client.post(OPERATION_URL, {
|
||||||
...pauseData
|
...reactivateData
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await req
|
const response = await req
|
||||||
|
|
||||||
|
// Creacion de la operacion inicial, antes de tener los datos
|
||||||
|
const operation: ObjeniousOperation = {
|
||||||
|
operation: "reactivate",
|
||||||
|
iccids: reactivateData.identifier.identifiers[0],
|
||||||
|
status: "noMassID",
|
||||||
|
request_id: response.data.requestId,
|
||||||
|
correlation_id: reactivateData.correlation_id
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Esto tiene poco sentido si la operacion ya se
|
||||||
|
// tenia que haber creado en el generador
|
||||||
|
this.logOperation(operation)
|
||||||
|
.then().catch(e => console.error("Error login operation", e))
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
console.log("[o] Sim solicitud de reactivacion ", response.data)
|
console.log("[o] Sim solicitud de reactivacion ", response.data)
|
||||||
return <Result<string, boolean>>{
|
return <Result<string, boolean>>{
|
||||||
@@ -223,7 +246,7 @@ export class SimUseCases {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[x] Error reactivacion", (error as AxiosError).response?.status)
|
console.error("[x] Error reactivacion", (error as AxiosError).response?.status)
|
||||||
return <Result<string, boolean>>{
|
return <Result<string, boolean>>{
|
||||||
error: "Error reactivando la sim" + pauseData.identifier,
|
error: "Error reactivando la sim" + reactivateData.identifier,
|
||||||
data: undefined
|
data: undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,6 +299,17 @@ export class SimUseCases {
|
|||||||
const correlation_id = suspendData.correlation_id
|
const correlation_id = suspendData.correlation_id
|
||||||
const iccid = suspendData.identifier.identifiers
|
const iccid = suspendData.identifier.identifiers
|
||||||
|
|
||||||
|
|
||||||
|
const operation: ObjeniousOperation = {
|
||||||
|
operation: "suspend",
|
||||||
|
iccids: iccid[0],
|
||||||
|
status: "running",
|
||||||
|
correlation_id: correlation_id
|
||||||
|
}
|
||||||
|
// No se registra hasta que no pase por la tabla de pausas
|
||||||
|
// this.logOperation(operation)
|
||||||
|
// .then().catch(e => console.error("Error login operation", e))
|
||||||
|
|
||||||
const fail = (error: string) => {
|
const fail = (error: string) => {
|
||||||
console.error("[Sim.usecases]", error)
|
console.error("[Sim.usecases]", error)
|
||||||
if (correlation_id != undefined) {
|
if (correlation_id != undefined) {
|
||||||
@@ -286,6 +320,14 @@ export class SimUseCases {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO REGISTRAR EL ORDER
|
||||||
|
if (correlation_id != undefined) {
|
||||||
|
await this.orderRepository.createOrder({
|
||||||
|
correlation_id: correlation_id,
|
||||||
|
order_type: "pause"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let activationDate;
|
let activationDate;
|
||||||
try {
|
try {
|
||||||
activationDate = await this.findActivationDate(suspendData)
|
activationDate = await this.findActivationDate(suspendData)
|
||||||
@@ -332,10 +374,11 @@ export class SimUseCases {
|
|||||||
public stage_terminate(terminateData: ActionData): () => Promise<Result<string, boolean>> {
|
public stage_terminate(terminateData: ActionData): () => Promise<Result<string, boolean>> {
|
||||||
return async (): Promise<Result<string, boolean>> => {
|
return async (): Promise<Result<string, boolean>> => {
|
||||||
const correlation_id = terminateData.correlation_id
|
const correlation_id = terminateData.correlation_id
|
||||||
|
const iccid = terminateData.identifier.identifiers[0]
|
||||||
|
|
||||||
const activationDate = await this.findActivationDate(terminateData)
|
const activationDate = await this.findActivationDate(terminateData)
|
||||||
const newTask: CreatePauseCancelTaskDTO = {
|
const newTask: CreatePauseCancelTaskDTO = {
|
||||||
iccid: terminateData.identifier.identifiers[0],
|
iccid: iccid,
|
||||||
activation_date: activationDate,
|
activation_date: activationDate,
|
||||||
next_check: undefined, // Que se haga instantaneamente al ser la primera
|
next_check: undefined, // Que se haga instantaneamente al ser la primera
|
||||||
operation_type: "terminate",
|
operation_type: "terminate",
|
||||||
@@ -344,6 +387,17 @@ export class SimUseCases {
|
|||||||
|
|
||||||
const taskCreated = await this.pauseRepository.addTask(newTask)
|
const taskCreated = await this.pauseRepository.addTask(newTask)
|
||||||
|
|
||||||
|
const operation: ObjeniousOperation = {
|
||||||
|
operation: "terminate",
|
||||||
|
iccids: iccid,
|
||||||
|
status: "running",
|
||||||
|
correlation_id: correlation_id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
this.logOperation(operation)
|
||||||
|
.then().catch(e => console.error("Error login operation", e))
|
||||||
|
*/
|
||||||
// Caso que la task no se pueda crear en la BDD
|
// Caso que la task no se pueda crear en la BDD
|
||||||
if (taskCreated.error != undefined) {
|
if (taskCreated.error != undefined) {
|
||||||
console.error("[Sim.usecases]", taskCreated.error)
|
console.error("[Sim.usecases]", taskCreated.error)
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export const rabbitmqEventBus = new RabbitMQEventBus({
|
|||||||
async function buildQueues(channel: Channel) {
|
async function buildQueues(channel: Channel) {
|
||||||
const QUEUES = {
|
const QUEUES = {
|
||||||
OBJ: "sim.objenious",
|
OBJ: "sim.objenious",
|
||||||
DLX: "sim.objenious.dlx",
|
OBJDLX: "sim.objenious.dlx",
|
||||||
DEL: "sim.objenious.delayed"
|
OBJDEL: "sim.objenious.delayed",
|
||||||
}
|
}
|
||||||
|
|
||||||
const EXCHANGES = {
|
const EXCHANGES = {
|
||||||
@@ -45,8 +45,8 @@ async function buildQueues(channel: Channel) {
|
|||||||
await channel.assertExchange(EXCHANGES.MAIN, "topic")
|
await channel.assertExchange(EXCHANGES.MAIN, "topic")
|
||||||
|
|
||||||
await channel.assertQueue(QUEUES.OBJ)
|
await channel.assertQueue(QUEUES.OBJ)
|
||||||
await channel.assertQueue(QUEUES.DLX)
|
await channel.assertQueue(QUEUES.OBJDLX)
|
||||||
await channel.assertQueue(QUEUES.DEL, {
|
await channel.assertQueue(QUEUES.OBJDEL, {
|
||||||
durable: true,
|
durable: true,
|
||||||
arguments: {
|
arguments: {
|
||||||
'x-message-ttl': DELAY,
|
'x-message-ttl': DELAY,
|
||||||
|
|||||||
@@ -131,6 +131,21 @@ export class SimController {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public reActivation() {
|
||||||
|
return this.controllerGenerator<{ iccid: string, offer: string }, { iccid: string, offer: string, compañia: string }>({
|
||||||
|
validator: iccidValidator,
|
||||||
|
mapBody: (b) => {
|
||||||
|
const { iccid, offer } = b
|
||||||
|
const compañia = companyFromIccid(iccid)
|
||||||
|
return { iccid, compañia, offer }
|
||||||
|
},
|
||||||
|
useCase: (args) => this.simUseCases.reActivation(args),
|
||||||
|
onError: (d, e) => console.error("[x] Error reactivacion: ", d, e),
|
||||||
|
onSuccess: console.log
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public cancelation() {
|
public cancelation() {
|
||||||
return this.controllerGenerator<{ iccid: string }, { iccid: string, compañia: string }>({
|
return this.controllerGenerator<{ iccid: string }, { iccid: string, compañia: string }>({
|
||||||
validator: iccidValidator,
|
validator: iccidValidator,
|
||||||
|
|||||||
@@ -130,6 +130,36 @@ export class SimUsecases {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async reActivation(args: { iccid: string, compañia: string, offer: string }):
|
||||||
|
Promise<Result<string, { iccid: string, message_id: string, operation: "reactivate" }>> {
|
||||||
|
const activationEvent = <SimEvents.activation>{
|
||||||
|
key: `sim.${args.compañia}.reactivate`,
|
||||||
|
payload: {
|
||||||
|
iccid: args.iccid,
|
||||||
|
offer: args.offer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const activationWithId = this.addMessage_id(activationEvent)
|
||||||
|
console.log("[d] Reactivation ", activationWithId)
|
||||||
|
await this.eventBus.publish([activationWithId])
|
||||||
|
const createdOrder = await this.saveOrder<SimEvents.reActivation>(activationWithId)
|
||||||
|
|
||||||
|
if (createdOrder.error != undefined) {
|
||||||
|
console.error(createdOrder.error)
|
||||||
|
return {
|
||||||
|
error: createdOrder.error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
iccid: args.iccid,
|
||||||
|
operation: "reactivate",
|
||||||
|
message_id: createdOrder.data?.correlation_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async preActivation(args: { iccid: string, compañia: string }):
|
async preActivation(args: { iccid: string, compañia: string }):
|
||||||
Promise<Result<string, { iccid: string, message_id: string, operation: "preactivation" }>> {
|
Promise<Result<string, { iccid: string, message_id: string, operation: "preactivation" }>> {
|
||||||
|
|
||||||
@@ -174,8 +204,10 @@ export class SimUsecases {
|
|||||||
|
|
||||||
const cancelationWithId = this.addMessage_id(cancelationEvent)
|
const cancelationWithId = this.addMessage_id(cancelationEvent)
|
||||||
console.log("[d] Cancelation ", cancelationWithId)
|
console.log("[d] Cancelation ", cancelationWithId)
|
||||||
|
|
||||||
await this.eventBus.publish([cancelationWithId])
|
await this.eventBus.publish([cancelationWithId])
|
||||||
const savedOrder = await this.saveOrder(cancelationWithId)
|
const savedOrder = await this.saveOrder(cancelationWithId)
|
||||||
|
|
||||||
if (savedOrder.error != undefined) {
|
if (savedOrder.error != undefined) {
|
||||||
console.error(savedOrder.error)
|
console.error(savedOrder.error)
|
||||||
return {
|
return {
|
||||||
@@ -205,11 +237,12 @@ export class SimUsecases {
|
|||||||
iccid: args.iccid
|
iccid: args.iccid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pauseWithId = this.addMessage_id(pauseEvent)
|
const pauseWithId = this.addMessage_id(pauseEvent)
|
||||||
console.log("[d] Pause", pauseWithId)
|
console.log("[d] Pause", pauseWithId)
|
||||||
await this.eventBus.publish([pauseWithId])
|
await this.eventBus.publish([pauseWithId])
|
||||||
await this.saveOrder(pauseWithId)
|
//await this.saveOrder(pauseWithId)
|
||||||
const savedOrder = await this.saveOrder(pauseWithId)
|
const savedOrder = await this.saveOrder<SimEvents.pause>(pauseWithId)
|
||||||
|
|
||||||
if (savedOrder.error != undefined) {
|
if (savedOrder.error != undefined) {
|
||||||
console.error(savedOrder.error)
|
console.error(savedOrder.error)
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ export const env = {
|
|||||||
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
||||||
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
||||||
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
||||||
|
CONNECTIONS_URL: String(process.env.CONNECTIONS_URL)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { simRoutes } from "./infrastructure/simRoutes.http.js"
|
|||||||
import { rabbitmqEventBus } from '#config/eventBusConfig.js';
|
import { rabbitmqEventBus } from '#config/eventBusConfig.js';
|
||||||
import { env } from "#config/env/index.js"
|
import { env } from "#config/env/index.js"
|
||||||
import { orderRoutes } from "#adapters/orderRoutes.http.js";
|
import { orderRoutes } from "#adapters/orderRoutes.http.js";
|
||||||
|
import { connectionsRoutes } from "#adapters/simconnectionsRoutes.js";
|
||||||
|
|
||||||
const PORT = env.API_PORT
|
const PORT = env.API_PORT
|
||||||
const HOSTNAME = "0.0.0.0"
|
const HOSTNAME = "0.0.0.0"
|
||||||
@@ -26,6 +27,7 @@ app.use(express.json());
|
|||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
app.use("/sim", simRoutes)
|
app.use("/sim", simRoutes)
|
||||||
|
app.use("/simconnections", connectionsRoutes)
|
||||||
app.use("/orders", orderRoutes)
|
app.use("/orders", orderRoutes)
|
||||||
|
|
||||||
app.use("/docs", express.static(path.join(process.cwd(), '../../docs')))
|
app.use("/docs", express.static(path.join(process.cwd(), '../../docs')))
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ simRoutes.get("/status", () => { })
|
|||||||
simRoutes.post("/save", simController.save())
|
simRoutes.post("/save", simController.save())
|
||||||
|
|
||||||
simRoutes.post("/activate", simController.activation())
|
simRoutes.post("/activate", simController.activation())
|
||||||
|
simRoutes.post("/reActivate", simController.reActivation())
|
||||||
|
|
||||||
simRoutes.post("/preActivate", simController.preactivation())
|
simRoutes.post("/preActivate", simController.preactivation())
|
||||||
|
|
||||||
@@ -35,4 +36,5 @@ simRoutes.post("/test", simController.test())
|
|||||||
// Proceso especifico de ALAI para liberar sims canceladas
|
// Proceso especifico de ALAI para liberar sims canceladas
|
||||||
simRoutes.post("/free", simController.free())
|
simRoutes.post("/free", simController.free())
|
||||||
|
|
||||||
|
|
||||||
export { simRoutes }
|
export { simRoutes }
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { env } from "#config/env/index.js"
|
||||||
|
import { Router } from "express"
|
||||||
|
import { ClientRequest, IncomingMessage } from "http"
|
||||||
|
import { createProxyMiddleware } from "http-proxy-middleware"
|
||||||
|
import { Request } from "express"
|
||||||
|
|
||||||
|
export const connectionsRoutes = Router()
|
||||||
|
|
||||||
|
const CONNECTIONS_URL = env.CONNECTIONS_URL// TODO: Meter al ENV
|
||||||
|
//const CONNECTIONS_URL = "http://sf-nfc-server.savefamilygps.net"
|
||||||
|
|
||||||
|
console.log("CONNURL: ", CONNECTIONS_URL)
|
||||||
|
|
||||||
|
connectionsRoutes.use("", createProxyMiddleware({
|
||||||
|
target: CONNECTIONS_URL,
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: {
|
||||||
|
'^/': "/simconnections/"
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
proxyReq: (proxyReq: ClientRequest, req: Request) => {
|
||||||
|
const protocol = req.protocol;
|
||||||
|
const host = req.get('host');
|
||||||
|
const originalFullUrl = `${protocol}://${host}${req.originalUrl}`;
|
||||||
|
const destinationFullUrl = `${CONNECTIONS_URL}${proxyReq.path}`;
|
||||||
|
/*
|
||||||
|
constnsole.log('──────────────────────────────────────────────────');
|
||||||
|
console.log(`[PROXY_DEBUG]`);
|
||||||
|
console.log(` ENTRADA: ${originalFullUrl}`);
|
||||||
|
console.log(` MÉTODO : ${req.method}`);
|
||||||
|
console.log(` DESTINO: ${destinationFullUrl}`);
|
||||||
|
console.log('──────────────────────────────────────────────────');
|
||||||
|
*/
|
||||||
|
console.log(`[Proxy Req]: ${req.method} ${req.url} -> ${proxyReq.path}`);
|
||||||
|
},
|
||||||
|
proxyRes: (proxyRes, req, res) => {
|
||||||
|
console.log(`[Proxy Res] Status: ${proxyRes.statusCode} desde ${req.url}`);
|
||||||
|
},
|
||||||
|
error: (err, req, res) => {
|
||||||
|
console.error('[Proxy Error]:', err);
|
||||||
|
|
||||||
|
// Validamos que 'res' tenga el método 'status' (típico de Express Response)
|
||||||
|
if ('status' in res) {
|
||||||
|
//@ts-ignore
|
||||||
|
res.status(500).json({ message: 'Error interno en el Gateway' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Rutas
|
||||||
|
/**
|
||||||
|
connectionsRoutes.post('/simconnections/alai/preactivate',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/pause',);
|
||||||
|
connectionsRoutes.post('/simconnections/alai/terminate',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/pauseByPhone',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/active',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/change_orderid',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/select',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/select-iccid',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/selectFromDb',);
|
||||||
|
connectionsRoutes.get('/simconnections/alai/selectPage',);
|
||||||
|
connectionsRoutes.post('/simconnections/alai/schedulePause',);
|
||||||
|
connectionsRoutes.get('/simconnections/shopify/getbyWP',);
|
||||||
|
connectionsRoutes.get('/simconnections/shopify/getbyWPS',);
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
connectionsRoutes.get('/simconnections/sim/associate',);
|
||||||
|
connectionsRoutes.post('/simconnections/sim/search',);
|
||||||
|
connectionsRoutes.post('/simconnections/sim/historic',);
|
||||||
|
connectionsRoutes.post('/simconnections/sim/update',);
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
connectionsRoutes.post('/simconnections/nos/activate',);
|
||||||
|
connectionsRoutes.get('/simconnections/nos/select',);
|
||||||
|
connectionsRoutes.get('/simconnections/nos/selectPage',);
|
||||||
|
|
||||||
|
//Unificación
|
||||||
|
connectionsRoutes.post('/simconnections/sim/active',); // True false
|
||||||
|
connectionsRoutes.patch('/simconnections/sim/pause',);
|
||||||
|
connectionsRoutes.get('/simconnections/sim/select',);
|
||||||
|
connectionsRoutes.get('/simconnections/sim/select-phone',);
|
||||||
|
**/
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
"cors": "*",
|
"cors": "*",
|
||||||
"dotenv": "*",
|
"dotenv": "*",
|
||||||
"express": "*",
|
"express": "*",
|
||||||
|
"http-proxy-middleware": "^3.0.5",
|
||||||
"sim-shared": "sim-shared:*",
|
"sim-shared": "sim-shared:*",
|
||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ async function startCron() {
|
|||||||
orderRepository
|
orderRepository
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await objTask.getPendingOperations()
|
||||||
const PERIODO_PETICIONES = 10 * 60 * 1000
|
const PERIODO_PETICIONES = 10 * 60 * 1000
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -80,7 +81,6 @@ async function startCron() {
|
|||||||
}
|
}
|
||||||
}, PERIODO_VOLCADO)
|
}, PERIODO_VOLCADO)
|
||||||
|
|
||||||
|
|
||||||
await pauseTask.run()
|
await pauseTask.run()
|
||||||
const PERIODO_CANCELACIONES = 60 * 60 * 1000;
|
const PERIODO_CANCELACIONES = 60 * 60 * 1000;
|
||||||
const clacelacionesInterval = setInterval(async () => {
|
const clacelacionesInterval = setInterval(async () => {
|
||||||
|
|||||||
@@ -118,9 +118,11 @@ export class PauseTerminateTask {
|
|||||||
|
|
||||||
switch (linea.status.billingStatus) {
|
switch (linea.status.billingStatus) {
|
||||||
case "ACTIVATED":
|
case "ACTIVATED":
|
||||||
let exito = false;
|
|
||||||
let result = null;
|
let result = null;
|
||||||
// IMPORTANTE COMRPOBAR EL DUE DATE
|
|
||||||
|
// Se termina el proceso aqui pero pasa a ser una operación de
|
||||||
|
// objenious por lo que puede fallar y quedaria registrado en
|
||||||
|
// la tabla objenious_operation
|
||||||
switch (operacionTipo) {
|
switch (operacionTipo) {
|
||||||
case "suspend":
|
case "suspend":
|
||||||
result = await this.simUsecases.suspend(actionData)()
|
result = await this.simUsecases.suspend(actionData)()
|
||||||
|
|||||||
25
packages/sim-rabbit-monitor/index.ts
Normal file
25
packages/sim-rabbit-monitor/index.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import express from "express"
|
||||||
|
import cors from 'cors';
|
||||||
|
import path from 'path';
|
||||||
|
import { env } from "packages/sim-shared/config/env/index.js"
|
||||||
|
|
||||||
|
const PORT = env.API_PORT
|
||||||
|
const HOSTNAME = "0.0.0.0"
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
app.use("/docs", express.static(path.join(process.cwd(), '../../docs')))
|
||||||
|
|
||||||
|
app.get("/health", (req, res) => {
|
||||||
|
res.status(200).json({ status: "ok" })
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(PORT, HOSTNAME, () => {
|
||||||
|
console.log("[o] Servidor iniciado en el puerto %d", PORT)
|
||||||
|
})
|
||||||
|
export default {}
|
||||||
15
packages/sim-shared/aplication/Rabbit.usecases.ts
Normal file
15
packages/sim-shared/aplication/Rabbit.usecases.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { RabbitManagementClient } from "../infrastructure/RabbitManagementClient.js";
|
||||||
|
import { Queue } from "../domain/Queue.js";
|
||||||
|
|
||||||
|
export class RabbitUseCases {
|
||||||
|
private client: RabbitManagementClient
|
||||||
|
|
||||||
|
constructor(client: RabbitManagementClient) {
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getQueuesStatus(): Promise<Queue[]> {
|
||||||
|
const queues = await this.client.getQueues()
|
||||||
|
return queues.sort((a, b) => b.messages - a.messages)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
packages/sim-shared/config/env/index.ts
vendored
Normal file
20
packages/sim-shared/config/env/index.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { loadEnvFile } from "node:process";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
|
||||||
|
loadEnvFile(path.join("../../.env")) // Global
|
||||||
|
|
||||||
|
export const env = {
|
||||||
|
ENVIRONMENT: process.env.ENVIORMENT,
|
||||||
|
API_PORT: parseInt(process.env.API_PORT ?? "3000"),
|
||||||
|
RABBITMQ_HOST: String(process.env.RABBITMQ_HOST ?? "localhost"),
|
||||||
|
RABBITMQ_USER: String(process.env.RABBITMQ_USER ?? "test"),
|
||||||
|
RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD ?? "test"),
|
||||||
|
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),
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js"
|
import { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js"
|
||||||
import { env } from "./env"
|
import { env } from "./env/index.js"
|
||||||
|
|
||||||
const rmqUser = env.RABBITMQ_USER
|
const rmqUser = env.RABBITMQ_USER
|
||||||
const rmqPass = env.RABBITMQ_PASSWORD
|
const rmqPass = env.RABBITMQ_PASSWORD
|
||||||
@@ -22,18 +22,5 @@ export const rabbitmqEventBus = new RabbitMQEventBus({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export async function startRMQClient() {
|
export async function startRMQClient() {
|
||||||
await rabbitmqEventBus.connect().catch(async e => {
|
|
||||||
console.error("Error en la conexion RMQ")
|
|
||||||
await rabbitmqEventBus.connect()
|
await rabbitmqEventBus.connect()
|
||||||
})
|
|
||||||
|
|
||||||
// Bindings especificos, deberia meterlos en la clase
|
|
||||||
try {
|
|
||||||
await rabbitmqEventBus.channel?.assertQueue("sim.nos")
|
|
||||||
} catch {
|
|
||||||
console.log("[i] Cola de sims de nos creada")
|
|
||||||
await rabbitmqEventBus.channel?.bindQueue("sim.nos", "sim.exchange", "sim.nos.*")
|
|
||||||
}
|
|
||||||
|
|
||||||
return rabbitmqEventBus
|
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,8 @@ export type FinishOrderDTO =
|
|||||||
IdOrCorrelationID
|
IdOrCorrelationID
|
||||||
&
|
&
|
||||||
{
|
{
|
||||||
reason?: string
|
reason?: string,
|
||||||
|
end_date?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ErrorOrderDTO =
|
export type ErrorOrderDTO =
|
||||||
|
|||||||
7
packages/sim-shared/domain/Queue.ts
Normal file
7
packages/sim-shared/domain/Queue.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface Queue {
|
||||||
|
name: string;
|
||||||
|
messages: number;
|
||||||
|
ready: number;
|
||||||
|
unacked: number;
|
||||||
|
consumers: number;
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ export namespace SimEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type reActivation = DomainEvent & {
|
export type reActivation = DomainEvent & {
|
||||||
key: `sim.${string}.reActivate`,
|
key: `sim.${string}.reactivate`,
|
||||||
payload: {
|
payload: {
|
||||||
iccid: string
|
iccid: string
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type ObjeniousOperation = {
|
|||||||
id?: number;
|
id?: number;
|
||||||
/** Uuid del mensaje asociado a la operacion */
|
/** Uuid del mensaje asociado a la operacion */
|
||||||
correlation_id?: string;
|
correlation_id?: string;
|
||||||
operation: "activate" | string; // TODO: completar y actualizar
|
operation: "activate" | "suspend" | "terminate" | string; // TODO: completar y actualizar
|
||||||
retry_count?: number;
|
retry_count?: number;
|
||||||
max_retry?: number;
|
max_retry?: number;
|
||||||
max_date_retry?: string | null;
|
max_date_retry?: string | null;
|
||||||
@@ -27,8 +27,7 @@ export type ObjeniousOperation = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ObjeniousOperationChange = {
|
export type ObjeniousOperationChange = {
|
||||||
id?: number;
|
id?: number; operation_id: number;
|
||||||
operation_id: number;
|
|
||||||
info?: string | null;
|
info?: string | null;
|
||||||
error?: string | null;
|
error?: string | null;
|
||||||
new_status: StatusEnum;
|
new_status: StatusEnum;
|
||||||
|
|||||||
@@ -1,6 +1,20 @@
|
|||||||
import { describe, it } from "node:test";
|
import { before, describe, it } from "node:test";
|
||||||
import { ObjeniousOperationsRepository } from "./ObjeniousOperationRepository.js";
|
import { ObjeniousOperationsRepository } from "./ObjeniousOperationRepository.js";
|
||||||
import { httpObjClient, postgresClient } from "../config/config.test.js";
|
import { httpObjClient, postgresClient } from "../config/config.test.js";
|
||||||
|
import { ObjeniousOperation } from "../domain/operationsRepository.port.js";
|
||||||
|
|
||||||
|
const correctOperation: ObjeniousOperation = {
|
||||||
|
iccids: "test",
|
||||||
|
operation: "activate",
|
||||||
|
status: "finished"
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorOperation: ObjeniousOperation = {
|
||||||
|
iccids: "test",
|
||||||
|
operation: "terminate",
|
||||||
|
status: "error",
|
||||||
|
error: "mensaje de error"
|
||||||
|
}
|
||||||
|
|
||||||
describe("[Integration] Test API requests", () => {
|
describe("[Integration] Test API requests", () => {
|
||||||
const repository = new ObjeniousOperationsRepository(
|
const repository = new ObjeniousOperationsRepository(
|
||||||
@@ -8,7 +22,18 @@ describe("[Integration] Test API requests", () => {
|
|||||||
postgresClient
|
postgresClient
|
||||||
)
|
)
|
||||||
|
|
||||||
it("Read /lines with multiple iccids", () => {
|
before(async () => {
|
||||||
|
await repository.createOperation(correctOperation)
|
||||||
|
await repository.createOperation(errorOperation)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Read last operation by line", () => {
|
||||||
|
/**
|
||||||
|
* Objetivo:
|
||||||
|
* - Cuando se va a hacer una operacion de sim hay que cancelarla directamente si:
|
||||||
|
* - Ya hay una en curso del mismo tipo.
|
||||||
|
* - Ya ha terminado una del mismo tipo.
|
||||||
|
* - Se ignoran las erroneas
|
||||||
|
*/
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -146,6 +146,20 @@ export class ObjeniousOperationsRepository implements IOperationsRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getLastOperationOfLine(iccid: string) {
|
||||||
|
const query = `
|
||||||
|
SELECT * FROM public.objenious_operation
|
||||||
|
WHERE iccids = $1 and error is null
|
||||||
|
ORDER BY id asc limit 1
|
||||||
|
`
|
||||||
|
const values = [iccid];
|
||||||
|
const { rows } = await this.pgClient.query(query, values);
|
||||||
|
return <Result<string, ObjeniousOperation>>{
|
||||||
|
data: rows[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async updateOperation(data: ObjeniousOperationChange): Promise<Result<string, ObjeniousOperation>> {
|
async updateOperation(data: ObjeniousOperationChange): Promise<Result<string, ObjeniousOperation>> {
|
||||||
const client = await this.pgClient.connect();
|
const client = await this.pgClient.connect();
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -302,8 +302,8 @@ export class OrderRepository {
|
|||||||
UPDATE order_tracking
|
UPDATE order_tracking
|
||||||
SET
|
SET
|
||||||
status = 'finished',
|
status = 'finished',
|
||||||
update_date = (now() at time zone 'utc'),
|
update_date = now(),
|
||||||
finish_date = (now() at time zone 'utc')
|
finish_date = now()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, status, update_date;
|
RETURNING id, status, update_date;
|
||||||
`
|
`
|
||||||
|
|||||||
37
packages/sim-shared/infrastructure/RabbitManagementClient.ts
Normal file
37
packages/sim-shared/infrastructure/RabbitManagementClient.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
import axios, { AxiosInstance } from "axios";
|
||||||
|
import { Queue } from "sim-shared/domain/Queue.js";
|
||||||
|
|
||||||
|
export class RabbitManagementClient {
|
||||||
|
private client: AxiosInstance;
|
||||||
|
|
||||||
|
constructor(args: {
|
||||||
|
baseURL: string;
|
||||||
|
user: string;
|
||||||
|
password: string;
|
||||||
|
}) {
|
||||||
|
this.client = axios.create({
|
||||||
|
baseURL: args.baseURL,
|
||||||
|
auth: {
|
||||||
|
username: args.user,
|
||||||
|
password: args.password,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getQueues(): Promise<Queue[]> {
|
||||||
|
try {
|
||||||
|
const response = await this.client.get("/queues")
|
||||||
|
return response.data.map((q: any) => ({
|
||||||
|
name: q.name,
|
||||||
|
messages: q.messages,
|
||||||
|
ready: q.messages_ready,
|
||||||
|
unacked: q.messages_unacknowledged,
|
||||||
|
consumers: q.consumers
|
||||||
|
}))
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[RabbitManagementClient] Error obteniendo colas", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { Request, Response } from "express";
|
||||||
|
import { DashboardUseCases } from "./Dashboard.usecases.js";
|
||||||
|
import { QueueSummary } from "#domain/Dashboard.js";
|
||||||
|
import { OrderTracking } from "packages/sim-shared/domain/Order.js";
|
||||||
|
|
||||||
|
export class DashboardController {
|
||||||
|
constructor(private readonly useCases: DashboardUseCases) {}
|
||||||
|
|
||||||
|
public async getQueuesFragment(req: Request, res: Response) {
|
||||||
|
const data = await this.useCases.getDashboardData()
|
||||||
|
res.send(renderQueuesHtml(data.queues))
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOrdersFragment(req: Request, res: Response) {
|
||||||
|
const data = await this.useCases.getDashboardData()
|
||||||
|
res.send(renderOrdersHtml(data.pendingOrders))
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getFullFragment(req: Request, res: Response) {
|
||||||
|
const data = await this.useCases.getDashboardData();
|
||||||
|
res.send(`
|
||||||
|
${renderQueuesHtml(data.queues)}
|
||||||
|
${renderOrdersHtml(data.pendingOrders)}
|
||||||
|
<p class="updated-at">Actualizado: ${new Date(data.generatedAt).toLocaleTimeString("es-ES")}</p>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueBadgeClass(count: number): string {
|
||||||
|
if (count === 0 ) return "badge--ok"
|
||||||
|
if (count < 10) return "badge--warn"
|
||||||
|
return "badge--error"
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderBadgeClass(status: string): string {
|
||||||
|
if (status === 'finished' ) return "badge--ok"
|
||||||
|
if (status == 'pending') return "badge--warn"
|
||||||
|
return "badge--error"
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderQueuesHtml(queues: QueueSummary): string {
|
||||||
|
const row = (label: string, q: typeof queues.main) => `
|
||||||
|
<tr>
|
||||||
|
<td>${label}</td>
|
||||||
|
<td>${q.name}</td>
|
||||||
|
<td><span class="badge ${queueBadgeClass(q.messages)}">${q.messages}</span></td>
|
||||||
|
<td>${q.ready}</td>
|
||||||
|
<td>${q.unacked}</td>
|
||||||
|
<td>${q.consumers}</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
return `
|
||||||
|
<section id="queues-section">
|
||||||
|
<h2>Estado de las colas</h2>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Tipo</th>
|
||||||
|
<th>Cola</th>
|
||||||
|
<th>Total</th>
|
||||||
|
<th>Ready</th>
|
||||||
|
<th>Unacked</th>
|
||||||
|
<th>Consumers</th>
|
||||||
|
<tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${row("Principal", queues.main)}
|
||||||
|
${row("Reintentos", queues.retry)}
|
||||||
|
${row("Fallidos (DLX)", queues.dlx)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOrdersHtml(orders: OrderTracking<unknown>[]): string {
|
||||||
|
const rows = orders.map(o => `
|
||||||
|
<tr>
|
||||||
|
<td>${o.id}</td>
|
||||||
|
<td>${o.correlation_id}</td>
|
||||||
|
<td>${o.order_type}</td>
|
||||||
|
<td><span class="badge ${orderBadgeClass(o.status)}">${o.status}</span></td>
|
||||||
|
<td>${o.retry_count ?? 0}</td>
|
||||||
|
<td>${new Date(o.start_date).toLocaleString("es-ES")}</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
).join("")
|
||||||
|
|
||||||
|
return `
|
||||||
|
<section id="orders-section">
|
||||||
|
<h2>Tareas pendientes (${orders.length})</h2>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Correlation ID</th>
|
||||||
|
<th>Tipo</th>
|
||||||
|
<th>Estado</th>
|
||||||
|
<th>Reintentos</th>
|
||||||
|
<th>Inicio</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>${rows}</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { RabbitManagementClient } from "packages/sim-shared/infrastructure/RabbitManagementClient.js";
|
||||||
|
import { OrderRepository } from "packages/sim-shared/infrastructure/OrderRepository.js";
|
||||||
|
import { DashboardData, QueueSummary } from "#domain/Dashboard.js";
|
||||||
|
import { Queue } from "packages/sim-shared/domain/Queue.js";
|
||||||
|
import { env } from "#config/env/index.js";
|
||||||
|
|
||||||
|
const EMPTY_QUEUE: Queue = { name: "", messages: 0, ready: 0, unacked: 0, consumers: 0}
|
||||||
|
|
||||||
|
export class DashboardUseCases {
|
||||||
|
constructor(
|
||||||
|
private readonly rabbitClient: RabbitManagementClient,
|
||||||
|
private readonly orderRepo: OrderRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async getDashboardData(): Promise<DashboardData> {
|
||||||
|
//si uno peta, no rompe al otro
|
||||||
|
const [queuesResult, pendingResult] = await Promise.allSettled([
|
||||||
|
this.rabbitClient.getQueues(),
|
||||||
|
this.orderRepo.getPendingOrders({ limit: 100 }), //por poner un límite
|
||||||
|
])
|
||||||
|
|
||||||
|
let queues: QueueSummary = { main: EMPTY_QUEUE, retry: EMPTY_QUEUE, dlx: EMPTY_QUEUE}
|
||||||
|
|
||||||
|
if (queuesResult.status === 'fulfilled') {
|
||||||
|
const all = queuesResult.value
|
||||||
|
const find = (name: string) => all.find(q => q.name === name) ?? {...EMPTY_QUEUE, name}
|
||||||
|
queues = {
|
||||||
|
main: find(env.QUEUE_MAIN!),
|
||||||
|
retry: find(env.QUEUE_RETRY!),
|
||||||
|
dlx: find(env.QUEUE_DLX!)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('[Dasboard] Error obteniendo colas: ', queuesResult.reason)
|
||||||
|
}
|
||||||
|
const pendingOrders = (pendingResult.status === "fulfilled" && !pendingResult.value.error)
|
||||||
|
? pendingResult.value.data ?? []
|
||||||
|
: [];
|
||||||
|
if (pendingResult.status === 'rejected') {
|
||||||
|
console.error('[Dashboard]Error obteniendo tareas: ', pendingResult.reason)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
queues,
|
||||||
|
pendingOrders,
|
||||||
|
generatedAt: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
packages/sim-visualizador-tareas-back/config/env/index.ts
vendored
Normal file
22
packages/sim-visualizador-tareas-back/config/env/index.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { loadEnvFile } from "node:process";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
loadEnvFile(path.join("../../env"))
|
||||||
|
|
||||||
|
export const env = {
|
||||||
|
API_PORT: parseInt(process.env.API_PORT ?? "3010"),
|
||||||
|
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),
|
||||||
|
RABBITMQ_PORT: process.env.RABBITMQ_PORT,
|
||||||
|
RABBITMQ_USER: String(process.env.RABBITMQ_USER),
|
||||||
|
RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD),
|
||||||
|
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
||||||
|
RABBITMQ_VHOST: process.env.RABBITMQ_VHOST,
|
||||||
|
QUEUE_MAIN: process.env.QUEUE_MAIN,
|
||||||
|
QUEUE_RETRY: process.env.QUEUE_RETRY,
|
||||||
|
QUEUE_DLX: process.env.QUEUE_DLX
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Pool } from "pg";
|
||||||
|
import { PgClient } from "packages/sim-shared/infrastructure/PgClient.js";
|
||||||
|
import { env } from "./env/index.js";
|
||||||
|
|
||||||
|
export const pgPool = new Pool({
|
||||||
|
user: env.POSTGRES_USER!,
|
||||||
|
host: env.POSTGRES_HOST!,
|
||||||
|
database: env.POSTGRES_DATABASE!,
|
||||||
|
password: env.POSTGRES_PASSWORD!,
|
||||||
|
port: Number(env.POSTGRES_PORT) || 5432
|
||||||
|
})
|
||||||
|
|
||||||
|
export const postgresClient = new PgClient({ pool: pgPool })
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { RabbitManagementClient } from "packages/sim-shared/infrastructure/RabbitManagementClient.js";
|
||||||
|
import { env } from "./env/index.js";
|
||||||
|
|
||||||
|
export const rabbitManagementClient = new RabbitManagementClient({
|
||||||
|
baseURL: env.RABBITMQ_HOST!,
|
||||||
|
user: env.RABBITMQ_USER!,
|
||||||
|
password: env.RABBITMQ_PASSWORD!
|
||||||
|
})
|
||||||
14
packages/sim-visualizador-tareas-back/domain/Dashboard.ts
Normal file
14
packages/sim-visualizador-tareas-back/domain/Dashboard.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Queue } from "packages/sim-shared/domain/Queue.js";
|
||||||
|
import { OrderTracking } from "packages/sim-shared/domain/Order.js";
|
||||||
|
|
||||||
|
export type QueueSummary = {
|
||||||
|
main: Queue;
|
||||||
|
retry: Queue;
|
||||||
|
dlx: Queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DashboardData {
|
||||||
|
queues: QueueSummary;
|
||||||
|
pendingOrders: OrderTracking<unknown>[];
|
||||||
|
generatedAt: string;
|
||||||
|
}
|
||||||
45
packages/sim-visualizador-tareas-back/index.ts
Normal file
45
packages/sim-visualizador-tareas-back/index.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import express from "express";
|
||||||
|
import cors from "cors";
|
||||||
|
import path from "path";
|
||||||
|
import { env } from "#config/env/index.js";
|
||||||
|
import { pgPool, postgresClient } from "#config/postgreConfig.js";
|
||||||
|
import { OrderRepository } from "packages/sim-shared/infrastructure/OrderRepository.js";
|
||||||
|
import { DashboardController } from "./aplication/Dashboard.controller.js";
|
||||||
|
import { DashboardUseCases } from "./aplication/Dashboard.usecases.js";
|
||||||
|
import { createDashboardRouter } from "#adapters/dashboardRoutes.http.js";
|
||||||
|
import { PgClient } from "packages/sim-shared/infrastructure/PgClient.js";
|
||||||
|
import { RabbitManagementClient } from "packages/sim-shared/infrastructure/RabbitManagementClient.js";
|
||||||
|
|
||||||
|
const pgClient = new PgClient({
|
||||||
|
pool: pgPool,
|
||||||
|
})
|
||||||
|
|
||||||
|
const rabbitClient = new RabbitManagementClient({
|
||||||
|
baseURL: env.RABBITMQ_HOST,
|
||||||
|
user: env.RABBITMQ_USER,
|
||||||
|
password: env.RABBITMQ_PASSWORD,
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderRepo = new OrderRepository(pgClient)
|
||||||
|
const useCases = new DashboardUseCases(rabbitClient, orderRepo)
|
||||||
|
const controller = new DashboardController(useCases)
|
||||||
|
const dashboardRouter = createDashboardRouter(controller)
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.static(path.join(process.cwd(), "../sim-visualizador-tareas-front")));
|
||||||
|
app.use("/", dashboardRouter);
|
||||||
|
app.get("/health", (_req, res) => res.status(200).json({ status: "ok" }));
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
await pgClient.checkDatabaseConnection();
|
||||||
|
app.listen(Number(env.API_PORT), "0.0.0.0", () => {
|
||||||
|
console.log("[o] Visualizador iniciado en el puerto %d", env.API_PORT);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error("[x] Error arrancando el visualizador:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { DashboardController } from "../aplication/Dashboard.controller.js";
|
||||||
|
|
||||||
|
export function createDashboardRouter(controller: DashboardController): Router {
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router.get("/fragments/queues", (req, res) => controller.getQueuesFragment(req, res))
|
||||||
|
router.get("/fragments/orders", (req, res) => controller.getOrdersFragment(req, res))
|
||||||
|
router.get("/fragments/dashboard", (req, res) => controller.getFullFragment(req, res))
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
72
packages/sim-visualizador-tareas-back/package.json
Normal file
72
packages/sim-visualizador-tareas-back/package.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"name": "sim-visualizador-tareas-back",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.ts",
|
||||||
|
"imports": {
|
||||||
|
"#config/*.js": {
|
||||||
|
"types": "./config/*.ts",
|
||||||
|
"default": "./config/*.js"
|
||||||
|
},
|
||||||
|
"#config/*": {
|
||||||
|
"types": "./config/*.ts",
|
||||||
|
"default": "./config/*.js"
|
||||||
|
},
|
||||||
|
"#adapters/*.js": {
|
||||||
|
"types": "./infrastructure/*.ts",
|
||||||
|
"default": "./infrastructure/*.js"
|
||||||
|
},
|
||||||
|
"#adapters/*": {
|
||||||
|
"types": "./infrastructure/*.ts",
|
||||||
|
"default": "./infrastructure/*.js"
|
||||||
|
},
|
||||||
|
"#domain/*.js": {
|
||||||
|
"types": "./domain/*.ts",
|
||||||
|
"default": "./domain/*.js"
|
||||||
|
},
|
||||||
|
"#domain/*": {
|
||||||
|
"types": "./domain/*.ts",
|
||||||
|
"default": "./domain/*.js"
|
||||||
|
},
|
||||||
|
"#ports/*.js": {
|
||||||
|
"types": "./ports/*.ts",
|
||||||
|
"default": "./ports/*.js"
|
||||||
|
},
|
||||||
|
"#ports/*": {
|
||||||
|
"types": "./ports/*.ts",
|
||||||
|
"default": "./ports/*.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --import tsx --test ./**/*.test.ts",
|
||||||
|
"build": "tsc --build && tsc-alias -p tsconfig.json && cp package.json ../../dist/packages/sim-visualizador-tareas-back/",
|
||||||
|
"dev": "tsx watch index.ts",
|
||||||
|
"start": "node ../../dist/packages/sim-visualizador-tareas-back/index.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"packageManager": "yarn@4.12.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@tsconfig/node22": "*",
|
||||||
|
"amqplib": "^0.10.9",
|
||||||
|
"axios": "*",
|
||||||
|
"cors": "*",
|
||||||
|
"dotenv": "*",
|
||||||
|
"express": "*",
|
||||||
|
"sim-shared": "sim-shared:*",
|
||||||
|
"typescript": "*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/amqplib": "^0.10.8",
|
||||||
|
"@types/cors": "*",
|
||||||
|
"@types/express": "*",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/supertest": "*",
|
||||||
|
"prettier": "*",
|
||||||
|
"supertest": "*",
|
||||||
|
"tsc-alias": "^1.8.16",
|
||||||
|
"tsx": "*",
|
||||||
|
"vitest": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
packages/sim-visualizador-tareas-back/tsconfig.json
Normal file
17
packages/sim-visualizador-tareas-back/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist",
|
||||||
|
"rootDir": "../../",
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
"../../packages/sim-shared/**/*.ts"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"config/env/index.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
0
packages/sim-visualizador-tareas-front/index.html
Normal file
0
packages/sim-visualizador-tareas-front/index.html
Normal file
8
sf-sim.code-workspace
Normal file
8
sf-sim.code-workspace
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
63
yarn.lock
63
yarn.lock
@@ -576,6 +576,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/http-proxy@npm:^1.17.15":
|
||||||
|
version: 1.17.17
|
||||||
|
resolution: "@types/http-proxy@npm:1.17.17"
|
||||||
|
dependencies:
|
||||||
|
"@types/node": "npm:*"
|
||||||
|
checksum: 10/893e46e12be576baa471cf2fc13a4f0e413eaf30a5850de8fdbea3040e138ad4171234c59b986cf7137ff20a1582b254bf0c44cfd715d5ed772e1ab94dd75cd1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/methods@npm:^1.1.4":
|
"@types/methods@npm:^1.1.4":
|
||||||
version: 1.1.4
|
version: 1.1.4
|
||||||
resolution: "@types/methods@npm:1.1.4"
|
resolution: "@types/methods@npm:1.1.4"
|
||||||
@@ -1154,7 +1163,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.4, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.3":
|
"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.3":
|
||||||
version: 4.4.3
|
version: 4.4.3
|
||||||
resolution: "debug@npm:4.4.3"
|
resolution: "debug@npm:4.4.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1413,6 +1422,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"eventemitter3@npm:^4.0.0":
|
||||||
|
version: 4.0.7
|
||||||
|
resolution: "eventemitter3@npm:4.0.7"
|
||||||
|
checksum: 10/8030029382404942c01d0037079f1b1bc8fed524b5849c237b80549b01e2fc49709e1d0c557fa65ca4498fc9e24cff1475ef7b855121fcc15f9d61f93e282346
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"expect-type@npm:^1.2.2":
|
"expect-type@npm:^1.2.2":
|
||||||
version: 1.3.0
|
version: 1.3.0
|
||||||
resolution: "expect-type@npm:1.3.0"
|
resolution: "expect-type@npm:1.3.0"
|
||||||
@@ -1527,6 +1543,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"follow-redirects@npm:^1.0.0":
|
||||||
|
version: 1.16.0
|
||||||
|
resolution: "follow-redirects@npm:1.16.0"
|
||||||
|
peerDependenciesMeta:
|
||||||
|
debug:
|
||||||
|
optional: true
|
||||||
|
checksum: 10/3fbe3d80b3b544c22705d837aa5d4a0d07a740d913534a2620b0a004c610af4148e3b58723536dd099aaa1c9d3a155964bde9665d6e5cb331460809a1fc572fd
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"follow-redirects@npm:^1.15.11":
|
"follow-redirects@npm:^1.15.11":
|
||||||
version: 1.15.11
|
version: 1.15.11
|
||||||
resolution: "follow-redirects@npm:1.15.11"
|
resolution: "follow-redirects@npm:1.15.11"
|
||||||
@@ -1788,6 +1814,31 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"http-proxy-middleware@npm:^3.0.5":
|
||||||
|
version: 3.0.5
|
||||||
|
resolution: "http-proxy-middleware@npm:3.0.5"
|
||||||
|
dependencies:
|
||||||
|
"@types/http-proxy": "npm:^1.17.15"
|
||||||
|
debug: "npm:^4.3.6"
|
||||||
|
http-proxy: "npm:^1.18.1"
|
||||||
|
is-glob: "npm:^4.0.3"
|
||||||
|
is-plain-object: "npm:^5.0.0"
|
||||||
|
micromatch: "npm:^4.0.8"
|
||||||
|
checksum: 10/83c1956be6451a5f4a2f3c7b3d84085dbd47e1efb5bb684c1ed668a6606c18c7c07be823b0dbba1326955b64cf88de2672492940b0b48d140215fbdb06105c9a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"http-proxy@npm:^1.18.1":
|
||||||
|
version: 1.18.1
|
||||||
|
resolution: "http-proxy@npm:1.18.1"
|
||||||
|
dependencies:
|
||||||
|
eventemitter3: "npm:^4.0.0"
|
||||||
|
follow-redirects: "npm:^1.0.0"
|
||||||
|
requires-port: "npm:^1.0.0"
|
||||||
|
checksum: 10/2489e98aba70adbfd8b9d41ed1ff43528be4598c88616c558b109a09eaffe4bb35e551b6c75ac42ed7d948bb7530a22a2be6ef4f0cecacb5927be139f4274594
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"https-proxy-agent@npm:^7.0.1":
|
"https-proxy-agent@npm:^7.0.1":
|
||||||
version: 7.0.6
|
version: 7.0.6
|
||||||
resolution: "https-proxy-agent@npm:7.0.6"
|
resolution: "https-proxy-agent@npm:7.0.6"
|
||||||
@@ -1865,7 +1916,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1":
|
"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1":
|
||||||
version: 4.0.3
|
version: 4.0.3
|
||||||
resolution: "is-glob@npm:4.0.3"
|
resolution: "is-glob@npm:4.0.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1881,6 +1932,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-plain-object@npm:^5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "is-plain-object@npm:5.0.0"
|
||||||
|
checksum: 10/e32d27061eef62c0847d303125440a38660517e586f2f3db7c9d179ae5b6674ab0f469d519b2e25c147a1a3bc87156d0d5f4d8821e0ce4a9ee7fe1fcf11ce45c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"is-promise@npm:^4.0.0":
|
"is-promise@npm:^4.0.0":
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
resolution: "is-promise@npm:4.0.0"
|
resolution: "is-promise@npm:4.0.0"
|
||||||
@@ -2842,6 +2900,7 @@ __metadata:
|
|||||||
cors: "npm:*"
|
cors: "npm:*"
|
||||||
dotenv: "npm:*"
|
dotenv: "npm:*"
|
||||||
express: "npm:*"
|
express: "npm:*"
|
||||||
|
http-proxy-middleware: "npm:^3.0.5"
|
||||||
prettier: "npm:*"
|
prettier: "npm:*"
|
||||||
sim-shared: "sim-shared:*"
|
sim-shared: "sim-shared:*"
|
||||||
supertest: "npm:*"
|
supertest: "npm:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user