Refactor error handling in routes to use centralized error creation. Added localized error messages for IP address and email validation in respective routes. Improved session retrieval error handling.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Router } from "express";
|
||||
import ActiveSession from "../models/ActiveSession";
|
||||
import { createError, ErrorCode } from "../utils/i18n";
|
||||
// import { isValidObjectId } from "mongoose";
|
||||
|
||||
const router = Router();
|
||||
@@ -8,18 +9,23 @@ router.get("/:id", async (req, res) => {
|
||||
const activeSessionId = req.params.id;
|
||||
|
||||
// if (!isValidObjectId(activeSessionId)) {
|
||||
// return res.json({ status: "error", message: "Invalid ObjectId" });
|
||||
// return res.json(createError(ErrorCode.INVALID_OBJECT_ID, req));
|
||||
// }
|
||||
|
||||
try {
|
||||
const activeSession = await ActiveSession.findById(activeSessionId);
|
||||
|
||||
if (!activeSession) {
|
||||
return res.json(createError(ErrorCode.SESSION_NOT_FOUND, req));
|
||||
}
|
||||
|
||||
await ActiveSession.findByIdAndUpdate(activeSessionId, {
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
res.json(activeSession);
|
||||
} catch (error) {
|
||||
res.json({ error: (error as Error).message });
|
||||
res.json(createError(ErrorCode.SESSION_FETCH_ERROR, req));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Router } from "express";
|
||||
import got from "got-cjs";
|
||||
import { createError, ErrorCode } from "../utils/i18n";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req, res) => {
|
||||
const ip = req.headers["x-real-ip"];
|
||||
|
||||
if (!ip)
|
||||
return res.json({
|
||||
error: "An error occurred while obtaining an IP address",
|
||||
});
|
||||
if (!ip) {
|
||||
return res.json(createError(ErrorCode.IP_ADDRESS_ERROR, req));
|
||||
}
|
||||
|
||||
try {
|
||||
const { countryCode }: { countryCode: string } = await got
|
||||
@@ -18,7 +18,7 @@ router.get("/", async (req, res) => {
|
||||
|
||||
res.json({ countryCode });
|
||||
} catch (error) {
|
||||
res.json({ error: (error as Error).message });
|
||||
res.json(createError(ErrorCode.COUNTRY_CODE_FETCH_ERROR, req));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Router } from "express";
|
||||
import nodemailer from "nodemailer";
|
||||
import { createError, ErrorCode, getLanguageFromRequest } from "../utils/i18n";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -9,6 +10,33 @@ router.post("/", async (req, res) => {
|
||||
console.log("email", email);
|
||||
console.log("link", link);
|
||||
|
||||
// Валидация входных данных
|
||||
if (!email) {
|
||||
return res.json(createError(ErrorCode.EMAIL_REQUIRED, req));
|
||||
}
|
||||
|
||||
if (!link) {
|
||||
return res.json(createError(ErrorCode.LINK_REQUIRED, req));
|
||||
}
|
||||
|
||||
const lang = getLanguageFromRequest(req);
|
||||
|
||||
// Локализованные тексты письма
|
||||
const emailContent = {
|
||||
EN: {
|
||||
subject: "Invitation to demonstration - stream.graff.tech",
|
||||
body: `<div>
|
||||
Link to connect to the demonstration: <a href="${link}" target="_blank">${link}</a>
|
||||
</div>`,
|
||||
},
|
||||
RU: {
|
||||
subject: "Приглашение на демонстрацию - stream.graff.tech",
|
||||
body: `<div>
|
||||
Ссылка для подключения к демонстрации: <a href="${link}" target="_blank">${link}</a>
|
||||
</div>`,
|
||||
},
|
||||
};
|
||||
|
||||
// create reusable transporter object using the default SMTP transport
|
||||
let transporter = nodemailer.createTransport({
|
||||
host: "mail.netangels.ru",
|
||||
@@ -25,16 +53,15 @@ router.post("/", async (req, res) => {
|
||||
await transporter.sendMail({
|
||||
from: "stream@graff.tech", // sender address
|
||||
to: email, // list of receivers
|
||||
subject: "Приглашение на демонстрацию - stream.graff.tech", // Subject line
|
||||
html: `<div>
|
||||
Ссылка для подключения к демонстрации: <a href="${link}" target="_blank">${link}</a>
|
||||
</div>`,
|
||||
subject: emailContent[lang].subject, // Subject line
|
||||
html: emailContent[lang].body,
|
||||
});
|
||||
|
||||
res.json({ ok: 1 });
|
||||
} catch (error) {
|
||||
console.log("error", (error as Error).message);
|
||||
res.json(createError(ErrorCode.EMAIL_SEND_ERROR, req));
|
||||
}
|
||||
|
||||
res.json({ ok: 1 });
|
||||
});
|
||||
|
||||
const sendInviteRouter = router;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Коды ошибок API
|
||||
* Этот файл можно скопировать на клиент для типизации ошибок
|
||||
*/
|
||||
|
||||
export enum ErrorCode {
|
||||
// General errors
|
||||
INTERNAL_ERROR = "INTERNAL_ERROR",
|
||||
|
||||
// Active Session errors
|
||||
INVALID_OBJECT_ID = "INVALID_OBJECT_ID",
|
||||
SESSION_NOT_FOUND = "SESSION_NOT_FOUND",
|
||||
SESSION_FETCH_ERROR = "SESSION_FETCH_ERROR",
|
||||
|
||||
// Country Code errors
|
||||
IP_ADDRESS_ERROR = "IP_ADDRESS_ERROR",
|
||||
COUNTRY_CODE_FETCH_ERROR = "COUNTRY_CODE_FETCH_ERROR",
|
||||
|
||||
// Email/Invite errors
|
||||
EMAIL_REQUIRED = "EMAIL_REQUIRED",
|
||||
LINK_REQUIRED = "LINK_REQUIRED",
|
||||
EMAIL_SEND_ERROR = "EMAIL_SEND_ERROR",
|
||||
}
|
||||
|
||||
/**
|
||||
* Интерфейс ответа с ошибкой
|
||||
*/
|
||||
export interface ErrorResponse {
|
||||
error: string;
|
||||
errorCode: ErrorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard для проверки, является ли ответ ошибкой
|
||||
*/
|
||||
export function isErrorResponse(response: any): response is ErrorResponse {
|
||||
return response && typeof response.error === "string" && typeof response.errorCode === "string";
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { Request } from "express";
|
||||
import { ErrorCode, ErrorResponse } from "../types/errorCodes";
|
||||
|
||||
type Language = "RU" | "EN";
|
||||
|
||||
interface Translations {
|
||||
[key: string]: {
|
||||
EN: string;
|
||||
RU: string;
|
||||
};
|
||||
}
|
||||
|
||||
const translations: Translations = {
|
||||
// General errors
|
||||
INTERNAL_ERROR: {
|
||||
EN: "Internal server error",
|
||||
RU: "Внутренняя ошибка сервера",
|
||||
},
|
||||
|
||||
// Active Session errors
|
||||
INVALID_OBJECT_ID: {
|
||||
EN: "Invalid ObjectId",
|
||||
RU: "Неверный идентификатор объекта",
|
||||
},
|
||||
SESSION_NOT_FOUND: {
|
||||
EN: "Session not found",
|
||||
RU: "Сессия не найдена",
|
||||
},
|
||||
SESSION_FETCH_ERROR: {
|
||||
EN: "Error fetching session",
|
||||
RU: "Ошибка получения сессии",
|
||||
},
|
||||
|
||||
// Country Code errors
|
||||
IP_ADDRESS_ERROR: {
|
||||
EN: "An error occurred while obtaining an IP address",
|
||||
RU: "Произошла ошибка при получении IP-адреса",
|
||||
},
|
||||
COUNTRY_CODE_FETCH_ERROR: {
|
||||
EN: "Error fetching country code",
|
||||
RU: "Ошибка получения кода страны",
|
||||
},
|
||||
|
||||
// Email/Invite errors
|
||||
EMAIL_REQUIRED: {
|
||||
EN: "Email is required",
|
||||
RU: "Требуется адрес электронной почты",
|
||||
},
|
||||
LINK_REQUIRED: {
|
||||
EN: "Link is required",
|
||||
RU: "Требуется ссылка",
|
||||
},
|
||||
EMAIL_SEND_ERROR: {
|
||||
EN: "Error sending email",
|
||||
RU: "Ошибка отправки письма",
|
||||
},
|
||||
EMAIL_SENT_SUCCESS: {
|
||||
EN: "Invitation sent successfully",
|
||||
RU: "Приглашение успешно отправлено",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Получает язык из заголовка X-User-Region
|
||||
* @param req - Express Request объект
|
||||
* @returns Код языка (EN или RU)
|
||||
*/
|
||||
export function getLanguageFromRequest(req: Request): Language {
|
||||
const region = req.headers["x-user-region"] as string;
|
||||
|
||||
// Поддерживаемые русскоязычные регионы
|
||||
const russianRegions = ["RU", "BY", "KZ", "UA"];
|
||||
|
||||
if (region && russianRegions.includes(region.toUpperCase())) {
|
||||
return "RU";
|
||||
}
|
||||
|
||||
return "EN";
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает переведенное сообщение
|
||||
* @param key - Ключ сообщения из translations
|
||||
* @param req - Express Request объект
|
||||
* @returns Переведенное сообщение
|
||||
*/
|
||||
export function t(key: string, req: Request): string {
|
||||
const lang = getLanguageFromRequest(req);
|
||||
|
||||
if (translations[key]) {
|
||||
return translations[key][lang];
|
||||
}
|
||||
|
||||
// Возвращаем ключ, если перевод не найден
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает переведенное сообщение для конкретного языка
|
||||
* @param key - Ключ сообщения из translations
|
||||
* @param lang - Код языка
|
||||
* @returns Переведенное сообщение
|
||||
*/
|
||||
export function tLang(key: string, lang: Language = "EN"): string {
|
||||
if (translations[key]) {
|
||||
return translations[key][lang];
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает объект ошибки с кодом и переведенным сообщением
|
||||
* @param errorCode - Код ошибки из ErrorCode enum
|
||||
* @param req - Express Request объект
|
||||
* @returns Объект с ошибкой и кодом ошибки
|
||||
*/
|
||||
export function createError(errorCode: ErrorCode, req: Request): ErrorResponse {
|
||||
return {
|
||||
error: t(errorCode, req),
|
||||
errorCode: errorCode,
|
||||
};
|
||||
}
|
||||
|
||||
export { ErrorCode } from "../types/errorCodes";
|
||||
export default { t, getLanguageFromRequest, tLang, createError };
|
||||
Reference in New Issue
Block a user