Se reciben webhooks simples desde el planificador

This commit is contained in:
2025-12-29 16:47:47 +01:00
parent a665703f46
commit 63343aae16
6 changed files with 66 additions and 38 deletions

View File

@@ -17,11 +17,11 @@ client.use(express.json({
client.use(express.urlencoded({ extended: true })); client.use(express.urlencoded({ extended: true }));
// Webhook reception // Webhook reception
client.post('/order/create', verifyWebhookSignature, handleWebhook); client.post('/order/create', handleWebhook);
// Health check // Health check
client.get('/health', (req, res) => { client.get('/health', (req, res) => {
res.status(200).send({ resp: 'OK' }); res.status(200).send({ resp: 'OK - Client' });
}); });
export default client; export default client;

View File

@@ -2,7 +2,7 @@
* Configuracion de los host iniciales y a que topics escuchan * Configuracion de los host iniciales y a que topics escuchan
*/ */
import { SetupSubscription, SubscriptionData } from "#controllers/subscriptions.types.js" import { ScheduledEvent, SetupSubscription, SubscriptionData } from "#controllers/subscriptions.types.js"
import { webhooksConfig } from "." import { webhooksConfig } from "."
@@ -22,12 +22,19 @@ export function generateSubscriptions(initialHosts?: SetupSubscription[]) {
...setup ...setup
}) })
} }
return topicSubscriberMap return topicSubscriberMap
} }
type InitialHost = {
topic: string,
host: string,
port: string,
endpoint: string,
method: "POST" | "PUT" | "DELETE",
secretkey: string,
}
export const INITIAL_HOSTS: SetupSubscription[] = [ export const INITIAL_HOSTS: InitialHost[] = [
{ {
topic: "order/create", topic: "order/create",
host: "localhost", host: "localhost",
@@ -35,11 +42,13 @@ export const INITIAL_HOSTS: SetupSubscription[] = [
endpoint: "/order/create", endpoint: "/order/create",
method: "POST", method: "POST",
secretkey: webhooksConfig.apiSecret || "1234", secretkey: webhooksConfig.apiSecret || "1234",
open: new Date,
options: {
period: 1000
}
} }
] ]
export const INITIAL_EVENTS: ScheduledEvent[] = [
{
topic: "order/create",
numberOfMessages: 5,
periodMs: 1000
}
]

View File

@@ -1,17 +1,22 @@
import { generateHMACSignature } from "#middleware/hmac.js"; import { generateHMACSignature } from "#middleware/hmac.js";
import { requestBuilder, shopifyHeaderBuilder } from "#shared/requests.js"; import { requestBuilder, shopifyHeaderBuilder } from "#shared/requests.js";
import { ShopifyEvent, SubscriptionData, SubscriptorData, Topic } from "./subscriptions.types"; import { ScheduledEvent, ShopifyEvent, SubscriptionData, SubscriptorData, Topic } from "./subscriptions.types";
/** /**
* De scheduler tiene poco * De scheduler tiene poco
* TODO:
* - No se pueden crear eventos nuevos
*/ */
export class EventScheduler { export class EventScheduler {
public eventList: SubscriptorData[] = [] public eventList: ScheduledEvent[] = []
public activeIntervals: NodeJS.Timeout[] = [] public activeIntervals: NodeJS.Timeout[] = []
public subscriptionManager: SubscriptionManager
constructor(args: { constructor(args: {
events?: SubscriptorData[], events?: ScheduledEvent[],
subscriptionManager: SubscriptionManager
}) { }) {
this.subscriptionManager = args.subscriptionManager
if (args.events != undefined) { if (args.events != undefined) {
this.eventList = args.events this.eventList = args.events
} }
@@ -20,16 +25,25 @@ export class EventScheduler {
private start() { private start() {
for (const event of this.eventList) { for (const event of this.eventList) {
console.log("Evento", event)
let sentMesages = 0 let sentMesages = 0
const interval = setInterval(() => { const interval = setInterval(() => {
console.log("[Server] Lanzado evento ", event) console.log("[Server] Lanzado evento ", event)
if (event.options?.mesages == undefined || event.options?.mesages < 1) if (event.numberOfMessages == undefined || event.numberOfMessages < 1) {
console.log("xx")
return; // Se lannza de continuo return; // Se lannza de continuo
}
this.subscriptionManager.poll(<ShopifyEvent>{
topic: event.topic,
data: { id: 123 }
})
sentMesages++; sentMesages++;
if (sentMesages > event.options.mesages) { if (sentMesages > event.numberOfMessages) {
clearInterval(interval) clearInterval(interval)
} }
}, event.options?.period ?? 1000) }, event.periodMs ?? 1000)
this.activeIntervals.push(interval) this.activeIntervals.push(interval)
} }
} }
@@ -38,13 +52,10 @@ export class EventScheduler {
export class SubscriptionManager { export class SubscriptionManager {
private subscriptions: Map<Topic, SubscriptionData> = new Map<Topic, SubscriptionData>() private subscriptions: Map<Topic, SubscriptionData> = new Map<Topic, SubscriptionData>()
public scheduler: EventScheduler
constructor( constructor(
scheduler: EventScheduler,
subs?: typeof this.subscriptions, subs?: typeof this.subscriptions,
) { ) {
this.scheduler = scheduler
if (subs != undefined) { if (subs != undefined) {
this.subscriptions = subs this.subscriptions = subs
} }
@@ -64,15 +75,18 @@ export class SubscriptionManager {
} }
public poll(event: ShopifyEvent) { public poll(event: ShopifyEvent) {
console.log("!! -- !!")
const topic = event.topic const topic = event.topic
if (!this.subscriptions.has(topic)) { if (!this.subscriptions.has(topic)) {
console.error("Topic desconocido: " + topic) console.error("[Server] Topic desconocido: " + topic)
return; return;
} }
const subscriptors = this.subscriptions.get(topic) const subscriptors = this.subscriptions.get(topic)
console.log("[Server] Enviando evento a ", subscriptors)
for (const sub of subscriptors!.subscriptors) { for (const sub of subscriptors!.subscriptors) {
const body = { const body = {
id: 1234 id: 1234
@@ -90,13 +104,14 @@ export class SubscriptionManager {
endpoint: sub.endpoint endpoint: sub.endpoint
}) })
request.write(parsedBody) request.write(parsedBody)
// Data puede venir en chunks! // Data puede venir en chunks!
request.on("data", () => console.log) request.on("data", () => console.log)
request.on("end", () => console.log) request.on("end", () => console.log)
request.on("error", () => console.error) request.on("error", () => console.error)
request.end() request.end()
console.debug("Enviado evento a ", sub.host, ":", sub.port, sub.endpoint) console.log("[Server] Enviado evento a ", sub.host, ":", sub.port, sub.endpoint)
} }
} }
} }

View File

@@ -10,14 +10,7 @@ export type SubscriptorData = {
port: string, port: string,
endpoint: string, endpoint: string,
secretkey: string, secretkey: string,
method: "POST" | "GET" | "PUT" | "DELETE" method: "POST" | "PUT" | "DELETE"
open: Date,
options?: {
/* En ms. Cada cuanto se envia un mensaje nuevo, para solo se envia un mensaje */
period?: number,
/* Numero de mensajes, para undefiend solo se manda uno, para <= 0 seran infinitos */
mesages?: number,
}
} }
export type ShopifyEvent = { export type ShopifyEvent = {
@@ -33,3 +26,9 @@ export type Topic = string
export type SetupSubscription = SubscriptorData & { export type SetupSubscription = SubscriptorData & {
topic: string topic: string
} }
export type ScheduledEvent = {
topic: string // Meter una lista de topics/plantilla
numberOfMessages?: number,
periodMs?: number
}

View File

@@ -1,21 +1,25 @@
import { Router } from 'express'; import { Router } from 'express';
import { ordersHandlerBuilder } from '#controllers/orders.webhook.js'; import { ordersHandlerBuilder } from '#controllers/orders.webhook.js';
import { subscriptonHandlerBuilder } from '#controllers/subscriptions.js'; import { subscriptonHandlerBuilder } from '#controllers/subscriptions.js';
import { generateSubscriptions } from '#config/initial_hosts.js'; import { generateSubscriptions, INITIAL_EVENTS, INITIAL_HOSTS } from '#config/initial_hosts.js';
import { EventScheduler, SubscriptionManager } from '#controllers/subscription_manager.js'; import { EventScheduler, SubscriptionManager } from '#controllers/subscription_manager.js';
const webhookRouter = Router(); const webhookRouter = Router();
const baseSubscriptions = generateSubscriptions() const initialSubscriptions = generateSubscriptions()
const subscriptionList = [...baseSubscriptions.values()].map(e => e.subscriptors).flat() const eventosIniciales = INITIAL_EVENTS
const scheduler = new EventScheduler({ events: subscriptionList })
const subscriptions = new SubscriptionManager(scheduler, baseSubscriptions) const subscriptionsManager = new SubscriptionManager(initialSubscriptions)
const scheduler = new EventScheduler({
events: eventosIniciales,
subscriptionManager: subscriptionsManager
})
// subto // subto
webhookRouter.post('/subto', subscriptonHandlerBuilder(subscriptions)); webhookRouter.post('/subto', subscriptonHandlerBuilder(subscriptionsManager));
// Simulacion de los webhook de shopify // Simulacion de los webhook de shopify
// Al llamar se supone que se genera el evento // Al llamar se supone que se genera el evento
webhookRouter.post('/shopify/orders', ordersHandlerBuilder(subscriptions)); webhookRouter.post('/shopify/orders', ordersHandlerBuilder(subscriptionsManager));
export default webhookRouter; export default webhookRouter;

View File

@@ -11,7 +11,8 @@ export function requestBuilder(args: {
const request = http.request({ const request = http.request({
host: args.host, host: args.host,
port: args.port, port: args.port,
path: args.endpoint path: args.endpoint,
method: args.method
}) })
Object.entries(args.headers).forEach(([name, value]) => { Object.entries(args.headers).forEach(([name, value]) => {