Skip to content
Merged
15 changes: 14 additions & 1 deletion client/src/components/Post/CreatePost.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ const CreatePost = () => {
try {
toast.loading("Creating post...");
const token = localStorage.getItem("token");
if (!token) {
toast.dismiss();
toast.error("You must be logged in to create a post.");
navigate("/login?redirect_to=/interview-experiences/create");
return;
}

const tagsArray = formData.tags.split(",").map((tag) => tag.trim());

// Convert compensation values to numbers, empty strings become null
Expand Down Expand Up @@ -293,7 +300,13 @@ const CreatePost = () => {
localStorage.removeItem("formData");
} catch (error) {
toast.dismiss();
toast.error("Failed to create the post.");
if (error.response?.status === 401) {
toast.error("Session expired! Please log in again.");
localStorage.removeItem("token");
navigate("/login?redirect_to=/interview-experiences/create");
} else {
toast.error(error.response?.data?.error || error.response?.data?.message || "Failed to create the post.");
}
console.error("Error creating post:", error);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch improves the create-post flow by handling unauthenticated users before the request is sent and by gracefully handling expired sessions with a clearer message and redirect. The error handling is more user-friendly and avoids leaving stale auth state behind. No blockers from this diff

Expand Down
14 changes: 13 additions & 1 deletion client/src/components/Post/EditPost.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@ const EditPost = () => {
try {
toast.loading("Updating post...");
const token = localStorage.getItem("token");
if (!token) {
toast.dismiss();
toast.error("You must be logged in to update a post.");
navigate("/login?redirect_to=/interview-experiences");
return;
}

const tagsArray = formData.tags;

Expand Down Expand Up @@ -334,7 +340,13 @@ const EditPost = () => {
navigate("/interview-experiences");
} catch (error) {
toast.dismiss();
toast.error("Failed to update the post.");
if (error.response?.status === 401) {
toast.error("Session expired! Please log in again.");
localStorage.removeItem("token");
navigate("/login?redirect_to=/interview-experiences");
} else {
toast.error(error.response?.data?.error || error.response?.data?.message || "Failed to update the post.");
}
console.error("Error updating post:", error);
}
};
Expand Down
18 changes: 18 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# Server Configuration
PORT=8081
MONGO_URL=mongodb+srv://<username>:<password>@cluster.mongodb.net/nexus
SECRET=your_jwt_secret_key_here
CORS_ORIGIN=http://localhost:5173

# Frontend URL (Used for generating reset password links, verification links, and email button links)
# Production: https://www.nexus-svnit.in
# Local dev: http://localhost:5173
FRONTEND_URL=http://localhost:5173

# Email Service (Gmail SMTP)
# 1. Use a Google Account with 2-Step Verification enabled
# 2. Generate a 16-character App Password at: https://myaccount.google.com/apppasswords
# 3. Enter your full Gmail address and the 16-character app password (without spaces)
EMAIL_ID=your-email@gmail.com
EMAIL_PASSWORD=your-16-character-app-password

# Permanent QR redirect destination for GET /qr
# Set separately in Vercel for each backend environment:
# - Stage backend (stage branch / Preview) -> test at https://stage.nexus-svnit.in/qr
Expand Down
23 changes: 14 additions & 9 deletions server/controllers/achievementController.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,22 @@ const addAchievement = async (req, res) => {

await Achievement.create(achievement);

const user = await userSchema.findOne({ admissionNumber: req.user.admissionNumber });
if (!user || !user.instituteEmail) {
return res.status(400).json({ message: "User email not found" });
// Send email notification (non-blocking)
try {
const user = await userSchema.findOne({ admissionNumber: req.user.admissionNumber });
const recipient = user?.instituteEmail || user?.personalEmail;
if (recipient) {
const emailContent = achievementSubmissionTemplate(user.fullName || 'Member');
await sendEmail({
to: recipient,
...emailContent,
critical: false
});
}
} catch (emailErr) {
console.error('Non-blocking error sending achievement notification email:', emailErr);
}

const emailContent = achievementSubmissionTemplate(user.fullName);
await sendEmail({
to: user.instituteEmail,
...emailContent
});

return res.status(200).json({
success: true,
message: "Waiting for Review"
Expand Down
36 changes: 21 additions & 15 deletions server/controllers/answerController.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,27 @@ const createAnswer = async (req, res) => {
question.answers.push(answer);
await question.save();

// Send email notification to the person who asked the question
if (question.askedBy.personalEmail && question.askedBy._id.toString() !== author) {
const answeredBy = await User.findById(author);
const emailContent = newAnswerTemplate(
question.askedBy,
question.postId.title,
question.question,
content,
answeredBy,
question.postId._id
);
await sendEmail({
to: question.askedBy.personalEmail,
...emailContent
});
// Send email notification to the person who asked the question (non-blocking)
try {
const recipient = question?.askedBy?.personalEmail || question?.askedBy?.instituteEmail;
if (recipient && question.askedBy._id.toString() !== author) {
const answeredBy = await User.findById(author);
const emailContent = newAnswerTemplate(
question.askedBy,
question.postId.title,
question.question,
content,
answeredBy,
question.postId._id
);
await sendEmail({
to: recipient,
...emailContent,
critical: false
});
}
} catch (emailErr) {
console.error("Non-blocking error sending answer notification email:", emailErr);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change improves reliability by ensuring answer notifications do not block the answer creation flow if email delivery fails. The fallback to instituteEmail and non-blocking error handling are sensible, and they reduce user-facing disruption without changing the main business logic.

// Populate the author details of the new answer
Expand Down
Loading
Loading