fix #1
@ -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
|
||||
|
||||
@ -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`.
|
||||
|
||||
72
server.js
72
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) {
|
||||
</html>
|
||||
`;
|
||||
|
||||
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}
|
||||
</html>
|
||||
`;
|
||||
|
||||
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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user