From 1fdd802c73446ca888fec09bd26131354b221772 Mon Sep 17 00:00:00 2001 From: romantarkin Date: Sun, 31 May 2026 21:03:11 +0500 Subject: [PATCH] fix --- .env.example | 2 ++ README.MD | 2 ++ server.js | 72 ++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 7546cd0..cad9785 100644 --- a/.env.example +++ b/.env.example @@ -17,3 +17,5 @@ SMTP_MAIL_FROM=noreply@example.com # Email отправителя API_PASS=your_secure_password_here # Пароль для доступа к API (используйте сложный!) PORT=3000 # Порт, на котором будет работать сервер HOST=0.0.0.0 # Хост для облачного деплоя +FNS_TIMEOUT_MS=30000 # Таймаут создания чека в ФНС +SMTP_TIMEOUT_MS=15000 # Таймаут отправки email diff --git a/README.MD b/README.MD index f4a878b..bf0cd52 100644 --- a/README.MD +++ b/README.MD @@ -43,6 +43,8 @@ SMTP_MAIL_FROM=noreply@example.com API_PASS=strong_api_password PORT=3000 HOST=0.0.0.0 +FNS_TIMEOUT_MS=30000 +SMTP_TIMEOUT_MS=15000 ``` `API_PASS` должен совпадать с `api_pass` в запросах к `POST /api/v1/create-receipt`. diff --git a/server.js b/server.js index cdc8eed..f0dcd90 100644 --- a/server.js +++ b/server.js @@ -19,6 +19,8 @@ app.use(express.json({ limit: "1mb" })); const PORT = process.env.PORT || 4000; const HOST = process.env.HOST || "0.0.0.0"; const MAX_RETRIES = 3; +const FNS_TIMEOUT_MS = Number(process.env.FNS_TIMEOUT_MS || 30000); +const SMTP_TIMEOUT_MS = Number(process.env.SMTP_TIMEOUT_MS || 15000); const ERROR_FILE = path.join(__dirname, "error.json"); const ADMIN_EMAIL = process.env.ADMIN_EMAIL; @@ -29,7 +31,10 @@ const transporter = nodemailer.createTransport({ auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS - } + }, + connectionTimeout: SMTP_TIMEOUT_MS, + greetingTimeout: SMTP_TIMEOUT_MS, + socketTimeout: SMTP_TIMEOUT_MS }); let nalogApi; @@ -57,10 +62,28 @@ function calculateTotal(items = []) { }, 0); } +function withTimeout(promise, timeoutMs, label) { + let timeoutId; + + const timeout = new Promise((_, reject) => { + timeoutId = setTimeout(() => { + reject(new Error(`${label} timeout after ${timeoutMs}ms`)); + }, timeoutMs); + }); + + return Promise.race([promise, timeout]).finally(() => { + clearTimeout(timeoutId); + }); +} + async function createReceiptWithRetry(income, retries = MAX_RETRIES) { for (let attempt = 1; attempt <= retries; attempt++) { try { - return await getNalogApi().addIncome(income); + return await withTimeout( + getNalogApi().addIncome(income), + FNS_TIMEOUT_MS, + "FNS receipt creation" + ); } catch (err) { console.error(`Попытка ${attempt} не удалась`, err.message || err); if (attempt === retries) throw err; @@ -118,12 +141,16 @@ async function notifyAdmin(errorData) { `; - await transporter.sendMail({ - from: process.env.SMTP_MAIL_FROM, - to: ADMIN_EMAIL, - subject: `Ошибка создания чека ${process.env.APPNAME}`, - html - }); + await withTimeout( + transporter.sendMail({ + from: process.env.SMTP_MAIL_FROM, + to: ADMIN_EMAIL, + subject: `Ошибка создания чека ${process.env.APPNAME}`, + html + }), + SMTP_TIMEOUT_MS, + "Admin email sending" + ); console.log(`Администратор ${ADMIN_EMAIL} уведомлен об ошибке`); } catch (err) { @@ -176,6 +203,19 @@ app.get("/health/deep", async (req, res) => { res.json(result); }); +app.get("/health/smtp", async (req, res) => { + try { + await withTimeout(transporter.verify(), SMTP_TIMEOUT_MS, "SMTP health check"); + res.json({ status: "ok", smtp: "ok" }); + } catch (err) { + res.status(500).json({ + status: "error", + smtp: "error", + message: err.message || "SMTP check failed" + }); + } +}); + app.post("/api/v1/create-receipt", async (req, res) => { try { const { api_pass, email, items } = req.body; @@ -260,12 +300,16 @@ ${process.env.APPNAME} `; - await transporter.sendMail({ - from: process.env.SMTP_MAIL_FROM, - to: email, - subject: `Чек ${process.env.APPNAME}`, - html - }); + await withTimeout( + transporter.sendMail({ + from: process.env.SMTP_MAIL_FROM, + to: email, + subject: `Чек ${process.env.APPNAME}`, + html + }), + SMTP_TIMEOUT_MS, + "Client email sending" + ); res.json({ success: true, -- 2.45.2