-
Notifications
You must be signed in to change notification settings - Fork 23
Feature/course categories #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
903436e
78c4c61
77543bb
ca44979
8cfa18e
ba285b7
2866f7b
8862d81
69a88df
08be6e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,26 +1,27 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| import axios from "axios"; | ||||||||||||||||||||||||||||||||||||||||||||||
| import logger from "../../src/config/logger.js"; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_SERVICE_ID = process.env.EMAILJS_SERVICE_ID || "service_5cyu19r"; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_TEMPLATE_ID = process.env.EMAILJS_TEMPLATE_ID || "template_ph4f0rl"; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_PRIVATE_KEY = process.env.EMAILJS_PRIVATE_KEY || "xsR_t0uapauk8d_6Tk0Y9"; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_PUBLIC_KEY = process.env.EMAILJS_PUBLIC_KEY || "p_hFoYPb6o0w7206-"; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_SERVICE_ID = process.env.EMAILJS_SERVICE_ID; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_TEMPLATE_ID = process.env.EMAILJS_TEMPLATE_ID; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_RECEIPT_TEMPLATE_ID = process.env.EMAILJS_RECEIPT_TEMPLATE_ID || EMAILJS_TEMPLATE_ID; | ||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Silent fallback to the OTP template for receipts risks sending the wrong email content. If 🤖 Prompt for AI AgentsSource: Path instructions |
||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_PRIVATE_KEY = process.env.EMAILJS_PRIVATE_KEY; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_PUBLIC_KEY = process.env.EMAILJS_PUBLIC_KEY; | ||||||||||||||||||||||||||||||||||||||||||||||
| const EMAILJS_API_URL = process.env.EMAILJS_API_URL || "https://api.emailjs.com/api/v1.0/email/send"; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const sendMail = async (otp, email) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!email || !otp) { | ||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Email and OTP are required to send the email."); | ||||||||||||||||||||||||||||||||||||||||||||||
| const sendTemplate = async (templateId, email, templateParams) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||
| process.env.NODE_ENV !== "test" && | ||||||||||||||||||||||||||||||||||||||||||||||
| (!EMAILJS_SERVICE_ID || !templateId || !EMAILJS_PUBLIC_KEY) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("EmailJS environment variables are not configured"); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| logger.info(`Sending OTP email to: ${email}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||
| const response = await axios.post(EMAILJS_API_URL, { | ||||||||||||||||||||||||||||||||||||||||||||||
| service_id: EMAILJS_SERVICE_ID, | ||||||||||||||||||||||||||||||||||||||||||||||
| template_id: EMAILJS_TEMPLATE_ID, | ||||||||||||||||||||||||||||||||||||||||||||||
| template_id: templateId, | ||||||||||||||||||||||||||||||||||||||||||||||
| user_id: EMAILJS_PUBLIC_KEY, | ||||||||||||||||||||||||||||||||||||||||||||||
| template_params: { | ||||||||||||||||||||||||||||||||||||||||||||||
| "otp": otp, | ||||||||||||||||||||||||||||||||||||||||||||||
| "email": email, | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| accessToken: EMAILJS_PRIVATE_KEY, | ||||||||||||||||||||||||||||||||||||||||||||||
| template_params: { ...templateParams, email }, | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| return { status: response.status, text: response.statusText }; | ||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -29,4 +30,18 @@ const sendMail = async (otp, email) => { | |||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export const sendOtpEmail = async (otp, email) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!email || !otp) throw new Error("Email and OTP are required to send the email."); | ||||||||||||||||||||||||||||||||||||||||||||||
| logger.info(`Sending OTP email to: ${email}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| return sendTemplate(EMAILJS_TEMPLATE_ID, email, { otp }); | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export const sendReceiptEmail = async (receipt) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!receipt.email || !receipt.txHash) throw new Error("Receipt email and transaction hash are required"); | ||||||||||||||||||||||||||||||||||||||||||||||
| logger.info(`Sending receipt for ${receipt.txHash} to: ${receipt.email}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| return sendTemplate(EMAILJS_RECEIPT_TEMPLATE_ID, receipt.email, receipt); | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+33
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Avoid logging raw email addresses.
🛡 Proposed fix- logger.info(`Sending OTP email to: ${email}`);
+ logger.info("Sending OTP email");
...
- logger.info(`Sending receipt for ${receipt.txHash} to: ${receipt.email}`);
+ logger.info(`Sending receipt for ${receipt.txHash}`);📝 Committable suggestion
Suggested change
🧰 Tools🪛 ast-grep (0.44.1)[warning] 34-34: Avoid logging sensitive data (log-sensitive-data) [warning] 40-40: Avoid logging sensitive data (log-sensitive-data) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const sendMail = sendOtpEmail; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export default sendMail; | ||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ import Session from "../models/Session.js"; | |
| import sendMail from "../../services/emails/sendMail.js"; | ||
| import { generatedOtp } from "../routes/emailRoutes.js"; | ||
| import logger from "../config/logger.js"; | ||
| import { enqueue } from "../jobs/queue.js"; | ||
|
|
||
| import { catchAsync, APIError } from "../middlewares/errorHandler.js"; | ||
|
|
||
|
|
@@ -111,14 +112,6 @@ export const registerUser = catchAsync(async (req, res, next) => { | |
| return next(new APIError("Email already exists", 400)); | ||
| } | ||
|
|
||
| // Send OTP email | ||
| try { | ||
| await sendMail(generatedOtp, email); | ||
| logger.info(`📧 OTP sent to: ${email}`); | ||
| } catch (error) { | ||
| logger.error(`Email sending failed for: ${email}`, error); | ||
| } | ||
|
|
||
| // Hash password | ||
| const hashedPassword = await bcrypt.hash(password, 12); | ||
|
|
||
|
|
@@ -130,6 +123,12 @@ export const registerUser = catchAsync(async (req, res, next) => { | |
| role: role || "student", | ||
| }); | ||
|
|
||
| await enqueue( | ||
| "sendOtpEmail", | ||
| { userId: user._id.toString(), otp: generatedOtp }, | ||
| { attempts: 5, backoffMs: 1000, idempotencyKey: `otp:${user._id}:${generatedOtp}` } | ||
| ); | ||
|
|
||
|
Comment on lines
+126
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify whether generatedOtp is defined anywhere in authController.js or delegated to a User model method.
rg -n 'generatedOtp' src/controllers/authController.js
rg -n -A5 -B5 'generateOtp|randomInt|otp' src/models/User.jsRepository: Deen-Bridge/dnb-backend Length of output: 372 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- authController.js outline ---'
ast-grep outline src/controllers/authController.js --view expanded || true
echo '--- emailRoutes.js outline ---'
ast-grep outline src/routes/emailRoutes.js --view expanded || true
echo '--- authController.js relevant lines ---'
sed -n '1,180p' src/controllers/authController.js | cat -n
echo '--- emailRoutes.js relevant lines ---'
sed -n '1,220p' src/routes/emailRoutes.js | cat -nRepository: Deen-Bridge/dnb-backend Length of output: 9386 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for path in ["src/controllers/authController.js","src/routes/emailRoutes.js"]:
p=Path(path)
print(f"\n## {path} ({sum(1 for _ in p.open())} lines)")
PYRepository: Deen-Bridge/dnb-backend Length of output: 252 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n## authController.js\n'
sed -n '1,170p' src/controllers/authController.js
printf '\n## emailRoutes.js\n'
sed -n '1,220p' src/routes/emailRoutes.jsRepository: Deen-Bridge/dnb-backend Length of output: 6517 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- search OTP usage ---'
rg -n "generatedOtp|otp" src | sed -n '1,200p'
echo
echo '--- emailRoutes.js with line numbers ---'
cat -n src/routes/emailRoutes.js
echo
echo '--- authController registerUser region ---'
sed -n '103,135p' src/controllers/authController.js | cat -nRepository: Deen-Bridge/dnb-backend Length of output: 3870 Generate a per-registration OTP here. 🧰 Tools🪛 ast-grep (0.44.1)[warning] 131-131: Avoid logging sensitive data (log-sensitive-data) 🤖 Prompt for AI Agents |
||
| logger.info(`✅ User registered successfully: ${email} (ID: ${user._id})`); | ||
|
|
||
| // Generate session and tokens | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Preserve or update the documented
seedcommand.Replacing
seedbreaks thenpm run seedcommand still documented in README.md. Restore the old alias or update the documentation and migration instructions together.🤖 Prompt for AI Agents