import Bree, { Job } from "bree"; import Graceful from "@ladjs/graceful"; import path from "path"; import { fileURLToPath } from "url"; import { CoreConfiguration } from "./configuration-manager.js"; import { sdkLogger as logger } from "../reporting-service/logger.js"; import { createConnectorFromPath } from "../../utils/connector-factory.js"; import { NatsLaborTicketListener } from "./nats-labor-ticket-listener.js"; type GracefulConstructor = new (config: { brees: Bree[] }) => { listen: () => void; }; const GracefulClass = Graceful as unknown as GracefulConstructor; export const runDataSyncService = async (connectorPath: string) => { const config = CoreConfiguration.inst(); try { // Create connector instance const connector = await createConnectorFromPath(connectorPath); // Start NATS listener if enabled if (process.env.NATS_ENABLED === "true") { const natsListener = new NatsLaborTicketListener(connector); await natsListener.start(); } // Find the SDK's node_modules directory and jobs path using ES module import const currentFileUrl = import.meta.url; const currentFilePath = fileURLToPath(currentFileUrl); // Navigate from current file to the SDK root and then to jobs // Current: /path/to/sdk/dist/mm-erp-sdk.js.js // Target: /path/to/sdk/dist/services/data-sync-service/jobs const sdkDistPath = path.dirname(currentFilePath); const jobsPath = path.join(sdkDistPath, "jobs"); const bree = new Bree({ root: jobsPath, logger, worker: { env: { CONNECTOR_PATH: connectorPath, // Pass through all required environment variables MM_MAPPING_SERVICE_URL: process.env.MM_MAPPING_SERVICE_URL, MM_MAPPING_AUTH_SERVICE_URL: process.env.MM_MAPPING_AUTH_SERVICE_URL, MM_MAPPING_SERVICE_TOKEN: process.env.MM_MAPPING_SERVICE_TOKEN, LOG_LEVEL: process.env.LOG_LEVEL, NODE_ENV: process.env.NODE_ENV, RETRY_ATTEMPTS: process.env.RETRY_ATTEMPTS, MM_API_RETRY_ATTEMPTS: process.env.MM_API_RETRY_ATTEMPTS, CACHE_TTL: process.env.CACHE_TTL, // Pass through all other environment variables that might be needed ...process.env, }, }, jobs: [ // { // Let the connector determine when to run migrations via ApplicationInitializer.initialize() // name: "run-migrations", // Running this once on startup will create the tables in the sqlite database // }, { name: "from-erp", timeout: "10s", interval: config.fromErpInterval, }, { name: "to-erp", //timeout: '3s', // Use timeout during development to see the job in action quickly interval: config.toErpInterval, }, { name: "retry-failed-labor-tickets", interval: config.retryLaborTicketsInterval, }, { name: "clean-up-expired-cache", interval: config.cacheExpirationCheckInterval, }, { name: "reconcile-work-orders", // The 1h timeout gives a deliberate first run shortly after each deployment; // without it, Bree's interval timer resets on restart and a container that // restarts more often than the interval would never run the job at all. // The worker no-ops (info log) for connectors that don't implement // reconcileWorkOrders(). timeout: "1 hour", interval: config.reconcileWorkOrdersInterval, }, ], }); logger.info( "\n================================INITIATING DATA SYNC CYCLES (Bree)================================\n" ); const jobsConfig = bree.config.jobs.map((job: Job) => ({ name: job.name, interval: job.interval, timeout: job.timeout, })); logger.info("JOBS CONFIGURATION:", { jobs: jobsConfig }); const graceful = new GracefulClass({ brees: [bree] }); graceful.listen(); (async () => { await bree.start(); })(); bree.on("jobStarted", (job) => { console.log("Job " + job.name + " started"); }); bree.on("jobCompleted", (job) => { if (job.error) { console.error("Job " + job.name + " failed:", job.error); } else { console.log("Job " + job.name + " completed successfully!"); } }); bree.on("error", (error) => { console.error("Bree error:", error); }); // Exit and let the jobs take over logger.info( "\n================================DATA SYNC CYCLES INITIATION COMPLETED================================\n" ); } catch (error) { logger.error("startUp: Error initiating data sync cycles:", error); } };