fix #3
@ -90,3 +90,7 @@ Content-Type: application/json
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Поле `email` обязательно: на этот адрес сервис отправит письмо со ссылкой на чек после успешного создания чека в ФНС.
|
||||
|
||||
Если чек создан в ФНС, но письмо клиенту не отправилось, API вернет `success: true`, `receiptCreated: true`, `emailSent: false`, `receiptId`, `printLink` и `technicalError`. Ошибка отправки сохранится в `error.json`.
|
||||
|
||||
110
server.js
110
server.js
@ -1,4 +1,5 @@
|
||||
import express from "express";
|
||||
import net from "net";
|
||||
import nodemailer from "nodemailer";
|
||||
import dotenv from "dotenv";
|
||||
import pkg from "lknpd-nalog-api";
|
||||
@ -80,6 +81,43 @@ function withTimeout(promise, timeoutMs, label) {
|
||||
});
|
||||
}
|
||||
|
||||
function formatTechnicalError(err) {
|
||||
return {
|
||||
message: err.message || "Unknown error",
|
||||
code: err.code,
|
||||
command: err.command,
|
||||
responseCode: err.responseCode,
|
||||
response: err.response
|
||||
};
|
||||
}
|
||||
|
||||
function smtpConfigForResponse() {
|
||||
return {
|
||||
host: process.env.SMTP_HOST,
|
||||
port: SMTP_PORT,
|
||||
secure: SMTP_SECURE,
|
||||
user: process.env.SMTP_USER,
|
||||
from: process.env.SMTP_MAIL_FROM
|
||||
};
|
||||
}
|
||||
|
||||
function checkTcpConnection(host, port, timeoutMs) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = net.createConnection({ host, port });
|
||||
|
||||
socket.setTimeout(timeoutMs);
|
||||
socket.once("connect", () => {
|
||||
socket.destroy();
|
||||
resolve();
|
||||
});
|
||||
socket.once("timeout", () => {
|
||||
socket.destroy();
|
||||
reject(new Error(`TCP connection timeout after ${timeoutMs}ms`));
|
||||
});
|
||||
socket.once("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
async function createReceiptWithRetry(income, retries = MAX_RETRIES) {
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
try {
|
||||
@ -208,14 +246,30 @@ app.get("/health/deep", async (req, res) => {
|
||||
});
|
||||
|
||||
app.get("/health/smtp", async (req, res) => {
|
||||
const smtp = smtpConfigForResponse();
|
||||
|
||||
try {
|
||||
await checkTcpConnection(process.env.SMTP_HOST, SMTP_PORT, SMTP_TIMEOUT_MS);
|
||||
} catch (err) {
|
||||
return res.status(500).json({
|
||||
status: "error",
|
||||
smtp: "error",
|
||||
step: "tcp_connect",
|
||||
config: smtp,
|
||||
technicalError: formatTechnicalError(err)
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await withTimeout(transporter.verify(), SMTP_TIMEOUT_MS, "SMTP health check");
|
||||
res.json({ status: "ok", smtp: "ok" });
|
||||
res.json({ status: "ok", smtp: "ok", config: smtp });
|
||||
} catch (err) {
|
||||
res.status(500).json({
|
||||
status: "error",
|
||||
smtp: "error",
|
||||
message: err.message || "SMTP check failed"
|
||||
step: "smtp_verify",
|
||||
config: smtp,
|
||||
technicalError: formatTechnicalError(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -304,19 +358,47 @@ ${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"
|
||||
);
|
||||
try {
|
||||
await withTimeout(
|
||||
transporter.sendMail({
|
||||
from: process.env.SMTP_MAIL_FROM,
|
||||
to: email,
|
||||
subject: `Чек ${process.env.APPNAME}`,
|
||||
html
|
||||
}),
|
||||
SMTP_TIMEOUT_MS,
|
||||
"Client email sending"
|
||||
);
|
||||
} catch (emailErr) {
|
||||
console.error("Чек создан, но email клиенту не отправлен:", emailErr);
|
||||
const technicalError = formatTechnicalError(emailErr);
|
||||
|
||||
await saveToErrorFile({
|
||||
type: "email_send_failed",
|
||||
email,
|
||||
items,
|
||||
amount: total,
|
||||
receiptId,
|
||||
printLink,
|
||||
error: emailErr.message || "Не удалось отправить email клиенту",
|
||||
technicalError
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
receiptCreated: true,
|
||||
emailSent: false,
|
||||
receiptId,
|
||||
printLink,
|
||||
warning: "Чек создан в ФНС, но письмо клиенту не отправлено. Данные сохранены в error.json.",
|
||||
technicalError
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
receiptCreated: true,
|
||||
emailSent: true,
|
||||
receiptId,
|
||||
printLink
|
||||
});
|
||||
@ -329,6 +411,7 @@ ${process.env.APPNAME}
|
||||
items: req.body?.items,
|
||||
amount: calculateTotal(req.body?.items),
|
||||
error: err.message || "Неизвестная ошибка",
|
||||
technicalError: formatTechnicalError(err),
|
||||
api_pass: req.body?.api_pass
|
||||
};
|
||||
|
||||
@ -337,7 +420,8 @@ ${process.env.APPNAME}
|
||||
|
||||
res.status(500).json({
|
||||
error: "Не удалось создать чек. Данные сохранены для повторной попытки.",
|
||||
saved_to_error_file: true
|
||||
saved_to_error_file: true,
|
||||
technicalError: formatTechnicalError(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user