Асинхронная функция зависает в связке с node.js

Поддержка
  • Приветствую!
    Подскажите пожалуйста что может быть?
    Есть Поток 1 в нем вызываю асинхронную функцию, а в ней есть код написан на node.js
    когда выполняется этот код, зависает основной поток. Но результат я получаю но в моменте выполнения висит поток 1 а потом крашиться..
    Версия BAS 28.1.2 версия Node.js v22.11.0
    Большое спасибо за ответы или советы!

    const fs = require("fs").promises;
    const path = require("path");
    const encoding = require("encoding");
    const { MongoClient } = require("mongodb");
    const crypto = require("crypto");
    
    // Импортируем функцию для подключения к базе данных
    const getConnect = require("../connectDB");
    
    // Конфигурационные параметры 
    const MONGOSERVER = process.env.MONGOSERVER || [[MONGOSERVER]];
    const DATABASE = process.env.DATABASE || [[DATABASE]];
    const PATCHCRONAS_BASE = process.env.PATCHCRONAS_BASE || [[PATCHCRONAS]];
    const SESSION_ID = process.env.SESSION_ID || [[SESSION_ID]];
    
    const PATCHCRONAS = path.join(PATCHCRONAS_BASE, "out", SESSION_ID);
    const folderPath = path.join(PATCHCRONAS, `${SESSION_ID}_files`);
    const filePath = path.join(PATCHCRONAS, `${SESSION_ID}.txt`);
    
    /**
     * Асинхронно ожидает появления файла в течение заданного таймаута.
     * @param {string} filePath - Полный путь к файлу.
     * @param {number} timeout - Таймаут в миллисекундах.
     * @param {number} pollInterval - Интервал проверки в миллисекундах.
     * @returns {Promise<string>} - Возвращает путь к файлу, если он появляется вовремя.
     * @throws {Error} - Если файл не появляется в течение таймаута.
     */
    async function waitForFile(filePath, timeout = 5 * 60 * 1000, pollInterval = 1000) {
      return new Promise((resolve, reject) => {
        const startTime = Date.now();
    
        const interval = setInterval(async () => {
          try {
            await fs.access(filePath);
            clearInterval(interval);
            resolve(filePath);
          } catch (err) {
            if (Date.now() - startTime > timeout) {
              clearInterval(interval);
              reject(new Error("Файл не появился в течение 5 минут."));
            }
            // Файл ещё не существует, продолжаем ожидание
          }
        }, pollInterval);
      });
    }
    
    /**
     * Сохраняет HTML-контент в базу данных MongoDB.
     * @param {string} sessionId - Идентификатор сессии.
     * @param {string} sectionName - Название секции.
     * @param {string} htmlContent - HTML-содержимое.
     */
    async function saveToDatabase(sessionId, sectionName, htmlContent) {
      try {
        const client = await getConnect(MONGOSERVER);
        const database = client.db(DATABASE);
    
        await database.collection("reports").findOneAndUpdate(
          { sessionId },
          {
            $push: {
              sections: {
                name: sectionName,
                htmlContent,
                hasData: true,
              },
            },
            $set: { updatedAt: new Date() },
          },
          { returnDocument: "after", upsert: true }
        );
    
        await client.close();
      } catch (error) {
        console.error("Ошибка при сохранении данных в базу:", error.message);
      }
    }
    
    /**
     * Вычисляет SHA-256 хэш файла асинхронно.
     * @param {string} filePath - Путь к файлу.
     * @returns {Promise<string|null>} - Возвращает хэш или null в случае ошибки.
     */
    async function getFileHash(filePath) {
      try {
        const fileBuffer = await fs.readFile(filePath);
        const hashSum = crypto.createHash("sha256");
        hashSum.update(fileBuffer);
        return hashSum.digest("hex");
      } catch {
        return null;
      }
    }
    
    /**
     * Извлекает имена файлов изображений из текста.
     * @param {string} text - Входной текст.
     * @returns {string[]} - Массив имен файлов изображений.
     */
    function extractImageFilenames(text) {
      const pattern = /Cro[A-Za-z0-9]{2,7}\.(jpg|png)/gi;
      return text.match(pattern) || [];
    }
    
    /**
     * Удаляет дубликаты изображений по хэшу асинхронно.
     * @param {string[]} imageFilenames - Имена файлов изображений.
     * @param {string} folderPath - Путь к папке с изображениями.
     * @returns {Promise<number>} - Количество удалённых дубликатов.
     */
    async function removeDuplicateImagesInOrder(imageFilenames, folderPath) {
      const imageHashes = new Map();
      let duplicatesRemoved = 0;
    
      for (const fileName of imageFilenames) {
        const filePath = path.join(folderPath, fileName);
        try {
          await fs.access(filePath);
        } catch {
          continue; // Файл не существует
        }
    
        const fileHash = await getFileHash(filePath);
        if (!fileHash) continue;
    
        if (imageHashes.has(fileHash)) {
          try {
            await fs.unlink(filePath);
            duplicatesRemoved++;
          } catch (err) {
            console.error(`Не удалось удалить файл ${filePath}:`, err.message);
          }
        } else {
          imageHashes.set(fileHash, fileName);
        }
      }
    
      return duplicatesRemoved;
    }
    
    /**
     * Заменяет имена файлов изображений в тексте на встроенные изображения в формате base64.
     * @param {string} inputText - Входной текст.
     * @param {string} folderPath - Путь к папке с изображениями.
     * @returns {string} - Обработанный текст с встроенными изображениями.
     */
    async function replaceImagesInText(inputText, folderPath) {
      let text = inputText;
      const pattern = /Cro[A-Za-z0-9]{2,7}\.(jpg|png)/gi;
      const matches = text.match(pattern);
    
      if (!matches) return text;
    
      for (const fileName of matches) {
        const filePath = path.join(folderPath, fileName);
        try {
          await fs.access(filePath);
          const fileBuffer = await fs.readFile(filePath);
          const base64Data = fileBuffer.toString("base64");
          const mimeType = fileName.endsWith(".png") ? "image/png" : "image/jpeg";
          const imgTag = `<div class="photo-container"><img src="data:${mimeType};base64,${base64Data}" alt="${fileName}"></div>`;
          const regex = new RegExp(fileName, "g");
          text = text.replace(regex, imgTag);
        } catch {
          // Удаляем строку с отсутствующим изображением
          const regex = new RegExp(`.*${fileName}.*\n?`, "gi");
          text = text.replace(regex, "");
        }
      }
    
      return text;
    }
    
    /**
     * Получает MIME-тип файла по расширению.
     * @param {string} filePath - Путь к файлу.
     * @returns {string} - MIME-тип файла.
     */
    function getMimeType(filePath) {
      const ext = path.extname(filePath).toLowerCase();
      switch (ext) {
        case ".jpg":
        case ".jpeg":
          return "image/jpeg";
        case ".png":
          return "image/png";
        default:
          return "application/octet-stream";
      }
    }
    
    /**
     * Очищает и форматирует текст.
     * @param {string} inputText - Входной текст.
     * @returns {string} - Очищенный и отформатированный текст.
     */
    function cleanText(inputText) {
      let cleanedText = inputText
        .replace(/[​-‍]/g, "")
        .replace(/[ \t]+/g, " ")
        .replace(/ *\n */g, "\n")
        .replace(/\n{2,}/g, "\n")
        .trim();
    
      const photosLinePattern = /(^|\n)\s*Фото:\s*(\n|$)/gi;
      cleanedText = cleanedText.replace(photosLinePattern, "");
    
      cleanedText = cleanedText.replace(/Ответ № (\d+)/g, "Лок № $1");
    
      const lines = cleanedText.split("\n");
      const uniqueLines = [];
    
      lines.forEach((line, index) => {
        const currentLine = line.trim().toLowerCase();
        const previousLine = index > 0 ? lines[index - 1].trim().toLowerCase() : null;
        if (index === 0 || currentLine !== previousLine) {
          uniqueLines.push(line);
        }
      });
    
      return uniqueLines.join("<br>");
    }
    
    /**
     * Обрабатывает текст и изображения, а затем сохраняет результаты в базу данных.
     * @param {string} inputText - Входной текст.
     * @param {string} folderPath - Путь к папке с изображениями.
     * @param {string} sessionId - Идентификатор сессии.
     */
    async function processTextAndImagesToDatabase(inputText, folderPath, sessionId) {
      try {
        const cleanedText = cleanText(inputText);
        const imageFilenames = extractImageFilenames(inputText);
    
        const folderExists = await fs.access(folderPath).then(() => true).catch(() => false);
        if (folderExists) {
          const duplicatesRemoved = await removeDuplicateImagesInOrder(imageFilenames, folderPath);
          console.log(`Удалено ${duplicatesRemoved} дубликатов изображений.`);
        }
    
        const finalText = await replaceImagesInText(cleanedText, folderPath);
    
        const htmlContent = `
    <!DOCTYPE html>
    <html lang="ru">
    <head>
        <meta charset="UTF-8">
        <title>Отчет</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                line-height: 1.6;
            }
            .photo-container {
                margin: 10px 0;
            }
            .photo-container img {
                max-width: 100%;
                height: auto;
                display: block;
            }
        </style>
    </head>
    <body>
        ${finalText}
    </body>
    </html>
        `;
    
        await saveToDatabase(sessionId, "cas", htmlContent);
        console.log("Данные успешно сохранены в базу данных.");
      } catch (error) {
        console.error("Ошибка обработки текста и изображений:", error.message);
      }
    }
    
    /**
     * Основная функция обработки.
     */
    async function main() {
      try {
        console.log("Ожидание появления файла...");
        await waitForFile(filePath);
        console.log("Файл обнаружен.");
    
        // Читаем файл как Buffer
        const fileBuffer = await fs.readFile(filePath);
    
        // Конвертация из Windows-1251 в UTF-8
        const convertedContent = encoding.convert(fileBuffer, "UTF-8", "WINDOWS-1251").toString();
    
        // Перезаписываем файл уже в UTF-8 (опционально, если необходимо)
        await fs.writeFile(filePath, convertedContent, "utf8");
    
        console.log("Файл успешно прочитан и преобразован.");
    
        // Обрабатываем текст и изображения, сохраняем в базу данных
        await processTextAndImagesToDatabase(convertedContent, folderPath, SESSION_ID);
      } catch (err) {
        console.error("Ошибка при обработке файла:", err.message);
      }
    }
    
    // Запуск основной функции
    main();