diff --git a/backend/.env.example b/backend/.env.example
index 7fa91722..6b8248aa 100644
--- a/backend/.env.example
+++ b/backend/.env.example
@@ -42,4 +42,12 @@ RAZORPAY_KEY_SECRET=your_razorpay_secret
# GEMINI KEY (Optional)
# ===========================================
# Get these from google ai studio
-GEMINI_API_KEY=YOUR_GEMINI_API_KEY
\ No newline at end of file
+GEMINI_API_KEY=YOUR_GEMINI_API_KEY
+
+# ===========================================
+# Email / OTP (Required for signup)
+# ===========================================
+# Use a Gmail account with an App Password:
+# https://myaccount.google.com/apppasswords
+EMAIL_USER=your_gmail@gmail.com
+EMAIL_PASS=your_app_password
\ No newline at end of file
diff --git a/backend/config/sendEmail.js b/backend/config/sendEmail.js
index 30db05dd..02804d7e 100644
--- a/backend/config/sendEmail.js
+++ b/backend/config/sendEmail.js
@@ -3,23 +3,41 @@ import dotenv from "dotenv";
dotenv.config();
+const emailConfigured =
+ Boolean(process.env.EMAIL_USER) && Boolean(process.env.EMAIL_PASS);
+
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
+ connectionTimeout: 10000,
+ greetingTimeout: 10000,
+ socketTimeout: 15000,
});
-transporter.verify((err) => {
- if (err) {
- console.log("SMTP ERROR =>", err);
- } else {
- console.log("SMTP SERVER READY");
- }
-});
+if (!emailConfigured) {
+ console.error(
+ "EMAIL_USER and EMAIL_PASS must be set for OTP email delivery.",
+ );
+} else {
+ transporter.verify((err) => {
+ if (err) {
+ console.log("SMTP ERROR =>", err);
+ } else {
+ console.log("SMTP SERVER READY");
+ }
+ });
+}
+
+export const isEmailConfigured = () => emailConfigured;
export const sendMail = async (email, htmlContent) => {
+ if (!emailConfigured) {
+ throw new Error("Email service is not configured");
+ }
+
try {
const info = await transporter.sendMail({
from: `"RIVETO" <${process.env.EMAIL_USER}>`,
@@ -33,4 +51,4 @@ export const sendMail = async (email, htmlContent) => {
console.error("Error sending email");
throw new Error("Email could not be sent");
}
-};
\ No newline at end of file
+};
diff --git a/backend/controller/authcontroller.js b/backend/controller/authcontroller.js
index ab0d157f..6ba92cc4 100644
--- a/backend/controller/authcontroller.js
+++ b/backend/controller/authcontroller.js
@@ -2,7 +2,7 @@ import User from "../model/userModel.js";
import validator from "validator";
import bcrypt from "bcryptjs";
import { genToken, genToken1 } from "../config/Token.js";
-import { sendMail } from "../config/sendEmail.js";
+import { sendMail, isEmailConfigured } from "../config/sendEmail.js";
import generateOTP from "../utils/otp.js";
import TempUser from "../model/tempUserModel.js";
import { otpTemplate } from "../utils/otpTemplet.js";
@@ -40,6 +40,16 @@ export const sendOTP = async (req, res) => {
otp,
otpExpire: new Date(Date.now() + 5 * 60 * 1000),
});
+
+ if (!isEmailConfigured()) {
+ await TempUser.deleteOne({ email });
+
+ return res.status(503).json({
+ success: false,
+ message: "Email service is not configured",
+ });
+ }
+
try {
await sendMail(email, otpTemplate(otp));
} catch (_error) {
diff --git a/frontend/public/_redirects b/frontend/public/_redirects
new file mode 100644
index 00000000..ad37e2c2
--- /dev/null
+++ b/frontend/public/_redirects
@@ -0,0 +1 @@
+/* /index.html 200
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 9acf5750..65843b0c 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -231,6 +231,7 @@ function App() {
{/* Public routes - Legal pages should be accessible without login */}
+ {submitError} +
+ )} {/* Login Link */} diff --git a/frontend/src/utils/apiConfig.js b/frontend/src/utils/apiConfig.js index a176dbe8..d4ddc5d6 100644 --- a/frontend/src/utils/apiConfig.js +++ b/frontend/src/utils/apiConfig.js @@ -26,6 +26,7 @@ const MESSAGE_MAP = { const apiConfig = axios.create({ baseURL: `${serverURL}/api`, withCredentials: true, + timeout: 30000, }); apiConfig.interceptors.response.use(