Viewing File: /home/ubuntu/efiexchange-node-base/src/app.ts

import "reflect-metadata";

import express, { Request, Response, NextFunction } from "express";
import logger from "./utils/logger";
import mainRoutes from "./routes";
import cors from "cors";
import fileUpload from "express-fileupload";
import methodOverride from "method-override";
import path from "path";
import fs from "fs";
import http from "http";
import { fetchExchangeRate, fetchGatewayExchangeRate, fetchSDKExchangeRate } from "./controllers/userApp/exchange.controller";
import { errorHandler } from '@shared/infra/http/middlewares/errorHandler'
import { headerHandler } from '@shared/infra/http/middlewares/headerHandler'

// Sentry Bitcoin 
import { sentryConfig } from '@config/sentry'
import * as Sentry from '@sentry/node'
import { fetchOTCExchangeRate } from "./controllers/exchange";
import { loadValidBinanceSymbols } from "./controllers/exchange/functions/providerSymbols";
import { loadValidOKXSymbols } from "./controllers/exchange/providers/okx";
import { stopAllPollingForUser } from "./controllers/exchange/providers/bvnk";
import { unsubscribeAllProviders } from "./controllers/exchange/functions/disconnectProviders";
import { sendUnsubscribe, sendDepthSubscribe, sendDepthUnsubscribe, sendTickerSubscribe, sendTickerUnsubscribe } from "./controllers/exchange/providers/binance"

const SocketIo = require("socket.io");

const port = process.env.NODE_SERVER_PORT || 9001;
const SSL_KEY = process.env.SSL_KEY;
const SSL_CERTIFICATE = process.env.SSL_CERTIFICATE;

const app = express();
sentryConfig(app)

let server;

const corsOpts = {
  origin: '*',
  methods: ['GET', 'POST'],
};

// Middleware for cross-origin requests
app.use(cors(corsOpts));

// Middleware for parsing requests
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(methodOverride());

app.use(Sentry.Handlers.errorHandler())


// Create HTTP or HTTPS server based on the presence of SSL certificates
if (SSL_KEY && SSL_CERTIFICATE) {
  const https = require('https');
  server = https.createServer({
    key: fs.readFileSync(SSL_KEY),
    cert: fs.readFileSync(SSL_CERTIFICATE),
  }, app);
} else {
  server = http.createServer(app);
}

// Extend Response interface to include custom methods
declare global {
  namespace Express {
    interface Response {
      sendResponse: (data: any, message: string) => void;
      sendError: (ex: any, ex_code?: number) => void;
      sendErrorCode: (ex: any, ex_code?: number) => void;
    }
  }
}

// Middleware for custom response helpers
app.use((req: Request, res: Response, next: NextFunction) => {

  // logger.info(" - - - - - - - - - -  BODY START - - - - - - - - - - ");
  // logger.info(JSON.stringify(req.body));
  // logger.info(" - - - - - - - - - -  BODY END - - - - - - - - - - ");

  logger.info(" - - - - - - - - - - - - - - - - - - - - - - - - - - ");


  // Success Response
  res.sendResponse = (data: any, message: string) => {
    return res.status(200).json({
      success: true,
      data,
      message,
      code: 200,
    });
  };

  // Failure Response
  res.sendError = (ex: any, ex_code: number = 200) => {
    return res.status(ex_code).json({
      success: false,
      error: ex,
      code: 200,
    });
  };

  // Failure with code
  res.sendErrorCode = (ex: any, ex_code: number) => {
    return res.status(200).json({
      success: false,
      error: ex,
      code: ex_code,
    });
  };

  next();
});

// Default home page route
app.get("/", (req: Request, res: Response) => {
  res.send("Welcome to Wallet App");
});

// Enable file uploads with security
app.use(
  fileUpload({
    createParentPath: true,
    limits: { fileSize: 10 * 1024 * 1024 }, // Limit file size to 10 MB
    abortOnLimit: true, // Abort request if file exceeds size limit
    safeFileNames: true, // Remove potentially harmful characters from filenames
  })
);

// Serve static files
app.use(express.static("public"));
app.use("/public", express.static(path.resolve(__dirname, "public")));

// API routes with Error Handlers
app.use(headerHandler)
app.use("/api", mainRoutes);
app.use(errorHandler)

// Catch 404 and forward to error handler
app.use((req: Request, res: Response) => {
  res.status(404).json({
    success: false,
    message: "The requested URL is not found",
    code: 404,
  });
});

// Centralized error-handling middleware
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  logger.error(err.message);
  res.status(500).json({
    success: false,
    error: err.message,
    code: 500,
  });
});

// Exchange Rate Socket

const io = SocketIo(server, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"]
  }
});

interface UserBinanceWS {
  ws: WebSocket;
  depthSubs: Set<string>;
}
const users: Record<string, UserBinanceWS> = {};

io.on("connection", (socket: any) => {
  console.log("New client connected:", socket.id);
  let monitoringSessions = {};

  socket.on("fetchConversionRate", (data: any) => {
    const { pair, type, currency_pair_id, user_configuration } = data;
    console.log(`pairs logger--------->`,pair); // USD-TRX,ETH-BNB,TRX-BTC
    if(!pair || pair.length === 0 || pair === "undefined" || pair === null) return;
    // const [fromCurrency, toCurrency] = pair.split("-");
    // console.log(`Fetch Exchange Rate For : ${fromCurrency}-${toCurrency}`);

    // Split the comma-separated pairs into an array
    const pairs = Array.isArray(pair) ? pair : pair.split(",").map((p: string) => p.trim());
    console.log("pair--------------->", pair);// USD-TRX,ETH-BNB,TRX-BTC

    // New Currencies with country code
    if (isNaN(currency_pair_id)) {
      stopAllPollingForUser(socket.id);
      unsubscribeAllProviders(socket.id);
    }

    // Loop through each currency pair
    pairs.forEach((p: string) => {
      const [fromCurrency, toCurrency] = p.split("-");
      console.log(`Exchange Rate For : ${fromCurrency}-${toCurrency}`);
      console.log("fetching started");
      if (fromCurrency.includes(":") || toCurrency.includes(":")) {
        console.log("currency includes :");
        let fiatCurrency;
        if (fromCurrency.includes(":")) {
          console.log("fromCurrency includes :");
          fiatCurrency = fromCurrency.split(":")[0];
        } else if (toCurrency.includes(":")) {
          console.log("toCurrency includes :");
          fiatCurrency = toCurrency.split(":")[0];
        }

        if (["USD", "EUR"].includes(fiatCurrency) && fiatCurrency.includes(":")) {
          console.log("fiatCurrency includes :");
          let fromCurrencyCode = fromCurrency.split(":")[0];
          let toCurrencyCode = toCurrency.split(":")[0];
          // Provider subscription
          fetchOTCExchangeRate(
            fromCurrencyCode,
            toCurrencyCode,
            socket.id,
            socket,
            type,
            p,
            user_configuration
          );
        } else {
          // API calls
          fetchExchangeRate(
            fromCurrency.replace(":", "-"),
            toCurrency.replace(":", "-"),
            socket.id,
            socket,
            type
          );
        }
      } else {
        // Provider subscription
        fetchOTCExchangeRate(
          fromCurrency,
          toCurrency,
          socket.id,
          socket,
          type,
          p,
          user_configuration
        );
      }
    });

  });

  socket.on("fetchExchangeRate", (data:any) => {
    const { pair, type, currency_pair_id, user_configuration } = data;
    const [fromCurrency, toCurrency] = pair.split("-");
    console.log(`Conversion Rate For : ${fromCurrency}-${toCurrency}`);

    // New Currencies with country code
    if(isNaN(currency_pair_id)){
      stopAllPollingForUser(socket.id);
      unsubscribeAllProviders(socket.id);
    }

    if((fromCurrency.includes(':') || toCurrency.includes(':'))) {
      let fiatCurrency;
      if(fromCurrency.includes(':')) {
        fiatCurrency = fromCurrency.split(':')[0];
      } else if(toCurrency.includes(':')) {
        fiatCurrency = toCurrency.split(':')[0];
      }
      if(["USD", "EUR"].includes(fiatCurrency)) {
        let fromCurrencyCode = fromCurrency.split(':')[0];
        let toCurrencyCode = toCurrency.split(':')[0];
        // Provider subscription
        fetchOTCExchangeRate(fromCurrencyCode, toCurrencyCode, socket.id, socket, type, currency_pair_id, user_configuration);
      } else {
        // API calls
        fetchExchangeRate(fromCurrency.replace(":", "-"), toCurrency.replace(":", "-"), socket.id, socket, type);
      }
    } else {
      // Provider subscription
      fetchOTCExchangeRate(fromCurrency, toCurrency, socket.id, socket, type, currency_pair_id, user_configuration);
    }

  });

  // used binance socket to fetch crypto rates
  socket.on("subcribeCryptoRates", (data: any) => {
    const {pair, user_id} = data;
    if (!pair || !user_id) return;
    sendTickerSubscribe(user_id, pair.toLowerCase(), socket);
  });

  socket.on("unsubscribeCryptoRates", (data: any) => {
    const {pair, user_id} = data;
    if (!pair || !user_id) return;
    sendTickerUnsubscribe(user_id, pair.toLowerCase(), socket);
  });

  socket.on("fetchSDKExchangeRate", (data: any) => {
    const { pair, user_id, type, apiKey, userId } = data;
    const [fromCurrency, toCurrency] = pair.split("-");
    console.log(`Fetch SDK Exchange Rate For : ${fromCurrency}-${toCurrency}`);
    fetchSDKExchangeRate(fromCurrency, toCurrency, user_id, socket, type, apiKey, userId);
  });

  socket.on("subcribeDepthRate", (data: any) => {
    const {pair, user_id} = data;
    if (!pair || !user_id) return;
    sendDepthSubscribe(user_id, pair.toLowerCase(), socket);
  });
  
  socket.on("unsubcribeDepthRate", (data: any) => {
    const {pair, user_id} = data;
    if (!pair || !user_id) return;
    sendDepthUnsubscribe(user_id, pair.toLowerCase(), socket);
  });


  socket.on("fetchGatewayExchangeRate", (data: any) => {
    const { pair, user_id, type, apiKey, userId } = data;
    const [fromCurrency, toCurrency] = pair.split("-");
    console.log(`Fetch Gateway Exchange Rate For : ${fromCurrency}-${toCurrency}`);
    fetchGatewayExchangeRate(fromCurrency, toCurrency, user_id, socket, type, apiKey, userId);
  });

  socket.on("closeProviders", (data: any) => {
    console.log("Closing providers manually")
    stopAllPollingForUser(socket.id);
    unsubscribeAllProviders(socket.id);
    // fetchOTCExchangeRate(fromCurrency, toCurrency, user_id, socket, type, currency_pair_id, user_configuration);
  });

  socket.on("disconnect", () => {
    stopAllPollingForUser(socket.id);
    unsubscribeAllProviders(socket.id);
    console.log("Client disconnected:", socket.id);
  });
});



// Server Initiation
server.listen(port, () => {
  try {
    (async () => {
      await loadValidBinanceSymbols()
      await loadValidOKXSymbols()
    })();
    logger.info(`Server is listening on port ${port}`);
  } catch (err) {
    process.exit(1);
  }
});



Back to Directory File Manager