jp-shared 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/configs/firebase-admin.js +24 -0
  2. package/configs/index.js +3 -0
  3. package/constants/configuration.js +6 -0
  4. package/constants/database.js +6 -0
  5. package/constants/index.js +6 -0
  6. package/constants/org.js +30 -0
  7. package/constants/time-constant.js +4 -0
  8. package/helpers/index.js +4 -0
  9. package/helpers/otp-helper.js +48 -0
  10. package/helpers/validateMongoId.js +13 -0
  11. package/index.js +15 -0
  12. package/models/admin-models/admin-model.js +62 -0
  13. package/models/admin-models/index.js +4 -0
  14. package/models/admin-models/token-model.js +20 -0
  15. package/models/candidate-models/candidateModel.js +375 -0
  16. package/models/candidate-models/index.js +8 -0
  17. package/models/candidate-models/mobileOTPModel.js +75 -0
  18. package/models/candidate-models/otpModel.js +75 -0
  19. package/models/candidate-models/requestModel.js +26 -0
  20. package/models/candidate-models/savedJobModel.js +20 -0
  21. package/models/candidate-models/searchAppearanceModel.js +50 -0
  22. package/models/candidate-models/tokenModel.js +20 -0
  23. package/models/common-models/course-model.js +20 -0
  24. package/models/common-models/downloadedResumeHistory-model.js +54 -0
  25. package/models/common-models/education-model.js +17 -0
  26. package/models/common-models/index.js +19 -0
  27. package/models/common-models/jobApplication-model.js +57 -0
  28. package/models/common-models/jobApproach-model.js +14 -0
  29. package/models/common-models/jobOffer-model.js +20 -0
  30. package/models/common-models/jobType-model.js +22 -0
  31. package/models/common-models/noticePeriod.js +14 -0
  32. package/models/common-models/paymentHistory-model.js +128 -0
  33. package/models/common-models/plan-model.js +144 -0
  34. package/models/common-models/role-model.js +22 -0
  35. package/models/common-models/searchAppearanceHistory-model.js +36 -0
  36. package/models/common-models/skill-model.js +21 -0
  37. package/models/common-models/sms-student-model.js +44 -0
  38. package/models/common-models/speakingLanguage.js +14 -0
  39. package/models/common-models/specialization-model.js +20 -0
  40. package/models/common-models/viewedProfileHistory-model.js +44 -0
  41. package/models/index.js +8 -0
  42. package/models/jobPost-models/active-model.js +71 -0
  43. package/models/jobPost-models/draft-model.js +42 -0
  44. package/models/jobPost-models/expired-model.js +46 -0
  45. package/models/jobPost-models/index.js +10 -0
  46. package/models/jobPost-models/invitation-model.js +72 -0
  47. package/models/jobPost-models/onHold-model.js +58 -0
  48. package/models/jobPost-models/pending-model.js +45 -0
  49. package/models/jobPost-models/rejected-model.js +51 -0
  50. package/models/jobPost-models/verification-model.js +50 -0
  51. package/models/organization-models/active-model.js +111 -0
  52. package/models/organization-models/index.js +4 -0
  53. package/models/organization-models/verification-model.js +52 -0
  54. package/models/recruiter-models/active-model.js +45 -0
  55. package/models/recruiter-models/basicDetail-model.js +113 -0
  56. package/models/recruiter-models/counter-model.js +13 -0
  57. package/models/recruiter-models/emailOTP-model.js +68 -0
  58. package/models/recruiter-models/index.js +10 -0
  59. package/models/recruiter-models/mobileOTP-model.js +88 -0
  60. package/models/recruiter-models/pending-model.js +44 -0
  61. package/models/recruiter-models/request-model.js +75 -0
  62. package/models/recruiter-models/token-model.js +15 -0
  63. package/models/recruiter-models/verification-model.js +63 -0
  64. package/package.json +28 -0
  65. package/schemes/address-schema.js +43 -0
  66. package/schemes/index.js +7 -0
  67. package/schemes/pendingJobPostStep1-schema.js +192 -0
  68. package/schemes/pendingJobPostStep2-scheme.js +117 -0
  69. package/schemes/pendingJobPostStep3-scheme.js +21 -0
  70. package/schemes/pendingJobPostStep4-scheme.js +22 -0
  71. package/utils/env-utils.js +12 -0
  72. package/utils/firebase-utils.js +220 -0
  73. package/utils/index.js +12 -0
  74. package/utils/invoiceTemplate.js +123 -0
  75. package/utils/otp-utils.js +23 -0
  76. package/utils/password-utils.js +49 -0
  77. package/utils/percentageWeights.js +106 -0
  78. package/utils/populate-utils.js +57 -0
  79. package/utils/sendEmail-utils.js +40 -0
  80. package/utils/sendInvoice-utils.js +139 -0
  81. package/utils/token-utils.js +18 -0
  82. package/utils/validate-utils.js +118 -0
@@ -0,0 +1,220 @@
1
+ const { Readable } = require("stream");
2
+ const { bucket } = require("../configs/firebase-admin");
3
+ const { validateFile, validateFilePath } = require("./validate-utils");
4
+
5
+ // Function to upload a file to Firebase Storage with size limit check
6
+ const uploadFileToFirebase = (file, file_path, fieldName) => {
7
+ return new Promise((resolve, reject) => {
8
+ const uploadedFile = file;
9
+
10
+ // console.log(uploadedFile, "uploading file-------------");
11
+ // console.log(uploadedFile.fieldName, "uploading file-------------");
12
+
13
+ // Check if file exists and matches the specified field name
14
+ if (!uploadedFile || uploadedFile.fieldname !== fieldName) {
15
+ reject(new Error(`Invalid ${fieldName} file.`));
16
+ return;
17
+ }
18
+
19
+ // Check if uploadedFile has a valid buffer property
20
+ if (!Buffer.isBuffer(uploadedFile.buffer)) {
21
+ reject(new Error(`Invalid buffer data for ${fieldName} file.`));
22
+ return;
23
+ }
24
+
25
+ // Check file size (1 MB limit)
26
+ const maxSizeInBytes = 1048576; // 1 MB in bytes
27
+ if (uploadedFile.size > maxSizeInBytes) {
28
+ reject(new Error(`File size exceeds the limit of 1 MB.`));
29
+ return;
30
+ }
31
+
32
+ const fileName = `${file_path}`;
33
+ const fileRef = bucket.file(fileName);
34
+ const fileStream = fileRef.createWriteStream({
35
+ metadata: {
36
+ contentType: uploadedFile.mimetype,
37
+ },
38
+ resumable: false,
39
+ });
40
+
41
+ const bufferStream = Readable.from([uploadedFile.buffer]);
42
+ bufferStream.pipe(fileStream);
43
+
44
+ fileStream.on("error", (err) => {
45
+ console.error(
46
+ `Error uploading ${fieldName} file to Firebase Storage:`,
47
+ err
48
+ );
49
+ reject(err); // Reject with the error for proper error propagation
50
+ });
51
+
52
+ fileStream.on("finish", () => {
53
+ console.log(`${fieldName} file upload finished.`);
54
+ resolve(fileRef); // Resolve with the file reference for further processing
55
+ });
56
+ });
57
+ };
58
+
59
+ // Function to get the download URL of a file from Firebase Storage
60
+ const getFirebaseUrl = (file) => {
61
+ return new Promise((resolve, reject) => {
62
+ file.getSignedUrl(
63
+ {
64
+ action: "read",
65
+ expires: "01-01-2100",
66
+ },
67
+ (err, url) => {
68
+ if (err) {
69
+ console.error(
70
+ "Error getting download URL from Firebase Storage:",
71
+ err
72
+ );
73
+ reject(err); // Reject with the error for proper error propagation
74
+ } else {
75
+ resolve(url); // Resolve with the download URL
76
+ }
77
+ }
78
+ );
79
+ });
80
+ };
81
+
82
+ // Generic function to store a file and get its Firebase URL with size limit check
83
+ const storeFileAndGetFirebaseUrl = async (file, file_path, fieldName) => {
84
+ try {
85
+ const uploadedFile = await uploadFileToFirebase(file, file_path, fieldName);
86
+ const downloadUrl = await getFirebaseUrl(uploadedFile);
87
+ return downloadUrl; // Return the download URL
88
+ } catch (error) {
89
+ throw error; // Throw the error for handling at the higher level
90
+ }
91
+ };
92
+
93
+ async function deleteFile(filePath) {
94
+ try {
95
+ await bucket.file(filePath).delete();
96
+ console.log(`File ${filePath} deleted successfully.`);
97
+ } catch (error) {
98
+ console.error("Error deleting file:", error);
99
+ }
100
+ }
101
+
102
+ async function moveFile(originalFilePath, newFilePath) {
103
+ try {
104
+ // Copy the file to the new location
105
+ await bucket.file(originalFilePath).copy(bucket.file(newFilePath));
106
+ console.log(`File moved from ${originalFilePath} to ${newFilePath}.`);
107
+
108
+ // Delete the original file
109
+ await bucket.file(originalFilePath).delete();
110
+ console.log(`File ${originalFilePath} deleted successfully.`);
111
+ } catch (error) {
112
+ console.error("Error moving file:", error);
113
+ }
114
+ }
115
+
116
+ async function downloadFile(fileName, destinationPath) {
117
+ try {
118
+ const file = bucket.file(fileName);
119
+ const [data] = await file.download();
120
+
121
+ // Write the downloaded data to a file
122
+ require("fs").writeFileSync(destinationPath, data);
123
+ console.log(`File ${fileName} downloaded successfully!`);
124
+ } catch (error) {
125
+ console.error("Error downloading file:", error);
126
+ }
127
+ }
128
+
129
+ // Example usage:
130
+ // deleteFile("images/my-image.jpg");
131
+
132
+ // Example usage:
133
+ // downloadFile("images/my-image.jpg", "./downloaded-image.jpg");
134
+
135
+ // Example usage:
136
+ // uploadFile("./my-image.jpg", "images/my-image.jpg");
137
+
138
+ function getFilePathFromFirebaseURL(url) {
139
+ try {
140
+ const parsedUrl = new URL(url);
141
+
142
+ // Extract the pathname, which includes the bucket name and file path
143
+ const fullPath = parsedUrl.pathname;
144
+
145
+ // Remove the bucket name (e.g., "a-b-c.appspot.com/") from the path
146
+ const bucketName = process.env.STORAGE_BUCKET_NAME;
147
+ const filePath = fullPath.replace(`/${bucketName}/`, "");
148
+ console.log(filePath, "this is a file................");
149
+ return filePath;
150
+ } catch (error) {
151
+ console.error("Error extracting file path:", error);
152
+ return null;
153
+ }
154
+ }
155
+
156
+ const uploadFile = async (file, filePath) => {
157
+ try {
158
+ validateFile(file); // Assuming these are implemented
159
+ validateFilePath(filePath);
160
+
161
+ const storageFile = bucket.file(filePath);
162
+ const stream = storageFile.createWriteStream({
163
+ metadata: {
164
+ contentType: file.mimetype,
165
+ },
166
+ resumable: false,
167
+ });
168
+
169
+ const bufferStream = Readable.from([file.buffer]);
170
+ bufferStream.pipe(stream);
171
+
172
+ return new Promise((resolve, reject) => {
173
+ stream.on("error", (err) => {
174
+ logger.error("Error uploading file to Firebase Storage:", { err });
175
+ reject(err);
176
+ });
177
+
178
+ stream.on("finish", async () => {
179
+ try {
180
+ const [url] = await storageFile.getSignedUrl({
181
+ action: "read",
182
+ expires: "01-01-2100",
183
+ });
184
+ resolve(url);
185
+ } catch (error) {
186
+ logger.error("Error generating signed URL:", { error });
187
+ reject(error);
188
+ }
189
+ });
190
+ });
191
+ } catch (error) {
192
+ logger.error("Error in uploadFile function:", { error: error.message });
193
+ throw error;
194
+ }
195
+ };
196
+
197
+ async function deleteFile(firebaseLink) {
198
+ if (!firebaseLink) {
199
+ throw new Error("File path is required.");
200
+ }
201
+
202
+ try {
203
+ const file = bucket.file(firebaseLink);
204
+ await file.delete();
205
+ console.log(`File at path ${firebaseLink} deleted successfully.`);
206
+ } catch (error) {
207
+ console.error(
208
+ `Error deleting file at path ${firebaseLink}: ${error.message}`
209
+ );
210
+ throw new Error("Failed to delete file from Firebase Storage.");
211
+ }
212
+ }
213
+
214
+ module.exports = {
215
+ storeFileAndGetFirebaseUrl,
216
+ getFirebaseUrl,
217
+ uploadFile,
218
+ deleteFile,
219
+ getFilePathFromFirebaseURL,
220
+ };
package/utils/index.js ADDED
@@ -0,0 +1,12 @@
1
+ module.exports = {
2
+ ...require("./env-utils"),
3
+ ...require("./percentageWeights"),
4
+ ...require("./sendEmail-utils"),
5
+ ...require("./validate-utils"),
6
+ ...require("./firebase-utils"),
7
+ ...require("./password-utils"),
8
+ ...require("./token-utils"),
9
+ ...require("./otp-utils"),
10
+ ...require("./populate-utils"),
11
+ ...require("./sendInvoice-utils"),
12
+ };
@@ -0,0 +1,123 @@
1
+ module.exports.invoiceEmailTemplate = ({
2
+ date = "06-12-2024",
3
+ billNo = "OAJPO1",
4
+ jobId = "JP24019",
5
+ items = [{ description: "Standard Plan", amount: 399 }],
6
+ totalAmount = "470.82",
7
+ totalAmountWords = "Four Hundred Seventy Rupees and Eighty-Two Paise Only",
8
+ gstPercentage = "18",
9
+ gstAmount = "6",
10
+ companyAddress,
11
+ companyName,
12
+ gstNumber,
13
+ logoUrl,
14
+ supportEmail,
15
+ supportMobile,
16
+ termsUrl,
17
+ }) => {
18
+ return `
19
+ <!DOCTYPE html>
20
+ <html lang="en">
21
+ <head>
22
+ <meta charset="UTF-8">
23
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
24
+ <title>Invoice</title>
25
+
26
+ </head>
27
+ <body>
28
+ <div style="max-width: 800px; margin: 0 auto; font-family: Arial, sans-serif; font-size: 14px; border: 1px solid #000; padding: 20px; background-color: #fff;">
29
+ <!-- Header Section -->
30
+ <div style="overflow: hidden; margin-bottom: 20px; width: 100%;">
31
+ <!-- Logo Section -->
32
+ <div style="float: left;">
33
+ <img src="${logoUrl}" alt="Company Logo" style="width: 150px; height: auto;" />
34
+ </div>
35
+ <!-- Invoice and Date Section -->
36
+ <div style="float: right; text-align: right;">
37
+ <h2 style="margin: 0;">Invoice</h2>
38
+ <p style="margin: 5px 0;"><strong>Date:</strong> ${date}</p>
39
+ </div>
40
+ </div>
41
+
42
+
43
+
44
+ <!-- Bill and Company Details -->
45
+ <div style="width: 100%; margin-bottom: 20px; font-size: 14px;">
46
+ <!-- Bill Details -->
47
+ <div style="display: inline-block; width: 48%; vertical-align: top;">
48
+ <p style="margin: 5px 0;"><strong>Bill No:</strong> ${billNo}</p>
49
+ <p style="margin: 5px 0;"><strong>Job ID:</strong> ${jobId}</p>
50
+ </div>
51
+ <!-- Company Name and Address -->
52
+ <div style="display: inline-block; width: 48%; text-align: right; vertical-align: top;">
53
+ <p style="margin: 5px 0;"><strong>${companyName}</strong></p>
54
+ <p style="margin: 5px 0;">${companyAddress}</p>
55
+ </div>
56
+ </div>
57
+
58
+
59
+ <!-- Table Section -->
60
+ <table style="width: 100%; border-collapse: collapse; margin-top: 20px; font-size: 14px;">
61
+ <thead>
62
+ <tr>
63
+ <th style="border: 1px solid #000; padding: 10px; background-color: #f4f4f4;">S. No</th>
64
+ <th style="border: 1px solid #000; padding: 10px; background-color: #f4f4f4;">Description of Service</th>
65
+ <th style="border: 1px solid #000; padding: 10px; background-color: #f4f4f4;">Amount</th>
66
+ </tr>
67
+ </thead>
68
+ <tbody>
69
+ ${items
70
+ .map(
71
+ (item, index) => `
72
+ <tr>
73
+ <td style="border: 1px solid #000; padding: 10px; text-align: center;">${
74
+ index + 1
75
+ }</td>
76
+ <td style="border: 1px solid #000; padding: 10px; text-align: center;">${
77
+ item.description
78
+ }</td>
79
+ <td style="border: 1px solid #000; padding: 10px; text-align: center;">${
80
+ item.amount
81
+ }</td>
82
+ </tr>`
83
+ )
84
+ .join("")}
85
+ <tr>
86
+ <td colspan="2" style="text-align: right; border: 1px solid #000; padding: 10px;">GST (${gstPercentage}%)</td>
87
+ <td style="border: 1px solid #000; padding: 10px; text-align: center;">${gstAmount}</td>
88
+ </tr>
89
+ <tr>
90
+ <td colspan="2" style="text-align: right; border: 1px solid #000; padding: 10px;"><strong>Total Amount</strong></td>
91
+ <td style="border: 1px solid #000; padding: 10px; text-align: center;"><strong>${totalAmount}</strong></td>
92
+ </tr>
93
+ </tbody>
94
+ </table>
95
+
96
+ <!-- Total Amount in Words -->
97
+ <div style="margin-top: 20px; text-align: right;">
98
+ <p style="margin: 5px 0;"><strong>Total in Words:</strong> ${totalAmountWords}</p>
99
+ </div>
100
+
101
+ <!-- Footer Section -->
102
+ <div style="margin-top: 30px; border-top: 1px solid #000; padding-top: 10px; font-size: 14px;">
103
+ <!-- Support and GST Details -->
104
+ <div style="width: 100%; overflow: hidden;">
105
+ <!-- Left Section -->
106
+ <div style="display: inline-block; width: 48%; vertical-align: top;">
107
+ <p style="margin: 5px 0;"><strong>Email:</strong> ${supportEmail}</p>
108
+ <p style="margin: 5px 0;"><strong>Mobile Number:</strong> ${supportMobile}</p>
109
+ <a href="${termsUrl}" target="_blank" style="text-decoration: none; color: blue;">Terms and conditions</a>
110
+ </div>
111
+ <!-- Right Section -->
112
+ <div style="display: inline-block; width: 48%; text-align: right; vertical-align: top;">
113
+ <p style="margin: 5px 0;"><strong>GST No:</strong> ${gstNumber}</p>
114
+ </div>
115
+ </div>
116
+ </div>
117
+
118
+ </div>
119
+
120
+ </body>
121
+ </html>
122
+ `;
123
+ };
@@ -0,0 +1,23 @@
1
+ function generateOTP(otpLength) {
2
+ // Validate otpLength
3
+ if (typeof otpLength !== "number" || otpLength < 1 || otpLength % 1 !== 0) {
4
+ throw new Error("Invalid OTP length. It must be a positive integer.");
5
+ }
6
+
7
+ // Calculate the range for OTP generation
8
+ const min = Math.pow(10, otpLength - 1);
9
+ const max = Math.pow(10, otpLength) - 1;
10
+
11
+ // Handle edge case when otpLength is 1
12
+ if (otpLength === 1) {
13
+ return Math.floor(Math.random() * 10).toString(); // Generates a single digit OTP (0-9)
14
+ }
15
+
16
+ // Generate the OTP number within the specified range
17
+ const otpNumber = Math.floor(Math.random() * (max - min + 1)) + min;
18
+
19
+ // Convert the number to a string and return
20
+ return otpNumber.toString();
21
+ }
22
+
23
+ module.exports = { generateOTP };
@@ -0,0 +1,49 @@
1
+ const bcrypt = require("bcrypt");
2
+
3
+ // Function to hash a password
4
+ async function hashPassword(plaintextPassword) {
5
+ try {
6
+ // Generate a salt
7
+ const saltRounds = 10;
8
+ const salt = await bcrypt.genSalt(saltRounds);
9
+
10
+ // Hash the password with the salt
11
+ const hashedPassword = await bcrypt.hash(plaintextPassword, salt);
12
+
13
+ return hashedPassword;
14
+ } catch (error) {
15
+ // Handle errors appropriately
16
+ console.error("Error hashing password:", error);
17
+ throw new Error("Password hashing failed");
18
+ }
19
+ }
20
+
21
+ // Function to compare a plaintext password with a hashed password
22
+ async function comparePassword(plaintextPassword, hashedPassword) {
23
+ // Input validation
24
+ if (
25
+ typeof plaintextPassword !== "string" ||
26
+ typeof hashedPassword !== "string"
27
+ ) {
28
+ throw new Error("Invalid input type");
29
+ }
30
+
31
+ if (!plaintextPassword || !hashedPassword) {
32
+ throw new Error("Passwords cannot be empty");
33
+ }
34
+
35
+ try {
36
+ // Compare the plaintext password with the hashed password
37
+ const isMatch = await bcrypt.compare(plaintextPassword, hashedPassword);
38
+ return isMatch;
39
+ } catch (error) {
40
+ console.error("Error comparing password:", error.message);
41
+ throw new Error("Password comparison failed");
42
+ }
43
+ }
44
+
45
+ // Export the function
46
+ module.exports = {
47
+ hashPassword,
48
+ comparePassword,
49
+ };
@@ -0,0 +1,106 @@
1
+ // Define percentage weights for all fields
2
+ const percentageWeights = {
3
+ resume: 15,
4
+ skillIds: 10,
5
+ roleIds: 10,
6
+ linkedIn: 3,
7
+ workStatus: 4,
8
+ noticePeriod: 4,
9
+ profileSummary: 5,
10
+ avatar: 5,
11
+ fullName: 5,
12
+ email: 5,
13
+ mobileNumber: 6,
14
+ countryCode: 2,
15
+ maritalStatus: 2,
16
+ gender: 2,
17
+ dob: 2,
18
+ address: 5,
19
+ speakingLanguageIds: 3,
20
+ educations: 5, // 5% for educations
21
+ schools: 5, // 5% for schools
22
+ projects: 7,
23
+ };
24
+
25
+ /**
26
+ * Calculates the score for a candidate based on provided fields.
27
+ * @param {Object} candidate - The candidate object containing various fields.
28
+ * @returns {number} The total score calculated based on the percentage weights.
29
+ */
30
+ const calculateScore = (candidate) => {
31
+ if (!candidate || typeof candidate !== "object") {
32
+ throw new Error("Invalid candidate object");
33
+ }
34
+
35
+ let totalScore = 0;
36
+
37
+ // Destructure properties for cleaner access
38
+ const {
39
+ resume,
40
+ skillIds,
41
+ roleIds,
42
+ linkedIn,
43
+ workStatus,
44
+ noticePeriod,
45
+ profileSummary,
46
+ avatar,
47
+ fullName,
48
+ email,
49
+ mobileNumber,
50
+ countryCode,
51
+ maritalStatus,
52
+ gender,
53
+ dob,
54
+ address,
55
+ speakingLanguageIds,
56
+ educations,
57
+ schools,
58
+ projects,
59
+ } = candidate;
60
+
61
+ // Add score for each field based on existence
62
+ if (resume) totalScore += percentageWeights.resume;
63
+ if (Array.isArray(skillIds) && skillIds.length)
64
+ totalScore += percentageWeights.skillIds;
65
+ if (Array.isArray(roleIds) && roleIds.length)
66
+ totalScore += percentageWeights.roleIds;
67
+ if (linkedIn) totalScore += percentageWeights.linkedIn;
68
+ if (workStatus) totalScore += percentageWeights.workStatus;
69
+ if (noticePeriod) totalScore += percentageWeights.noticePeriod;
70
+ if (profileSummary) totalScore += percentageWeights.profileSummary;
71
+ if (avatar) totalScore += percentageWeights.avatar;
72
+ if (fullName) totalScore += percentageWeights.fullName;
73
+ if (email) totalScore += percentageWeights.email;
74
+ if (mobileNumber) totalScore += percentageWeights.mobileNumber;
75
+ if (countryCode) totalScore += percentageWeights.countryCode;
76
+ if (maritalStatus) totalScore += percentageWeights.maritalStatus;
77
+ if (gender) totalScore += percentageWeights.gender;
78
+ if (dob) totalScore += percentageWeights.dob;
79
+ if (address) totalScore += percentageWeights.address;
80
+ if (Array.isArray(speakingLanguageIds) && speakingLanguageIds.length)
81
+ totalScore += percentageWeights.speakingLanguageIds;
82
+
83
+ // Check for either educations or schools
84
+ if (
85
+ (Array.isArray(educations) && educations.length) ||
86
+ (Array.isArray(schools) && schools.length)
87
+ ) {
88
+ totalScore += percentageWeights.educations; // Add 5% for educations if exists
89
+ }
90
+ // if (
91
+ // Array.isArray(schools) &&
92
+ // schools.length &&
93
+ // !(Array.isArray(educations) && educations.length)
94
+ // ) {
95
+ // totalScore += percentageWeights.schools; // Add 5% for schools only if educations don't exist
96
+ // }
97
+
98
+ // Add score for projects if they exist
99
+ if (Array.isArray(projects) && projects.length) {
100
+ totalScore += percentageWeights.projects;
101
+ }
102
+
103
+ return totalScore;
104
+ };
105
+
106
+ module.exports = { percentageWeights, calculateScore };
@@ -0,0 +1,57 @@
1
+ const { SkillModel } = require("../models/common-models/skill-model");
2
+ const { RoleModel } = require("../models/common-models/role-model");
3
+ const {
4
+ ActiveOrganizationModel,
5
+ } = require("../models/organization-models/active-model");
6
+ const { EducationModel } = require("../models/common-models/education-model");
7
+ const {
8
+ SpecializationModel,
9
+ } = require("../models/common-models/specialization-model");
10
+ const { CourseModel } = require("../models/common-models/course-model");
11
+
12
+ /**
13
+ * Applies common population and selection to job post queries.
14
+ * @param {Query} query - The Mongoose query to modify.
15
+ * @returns {Query} - The modified Mongoose query.
16
+ */
17
+ const populateJobPostDetails = (query) => {
18
+ return query
19
+ .select("-createdAt -updatedAt -__v -planId") // Exclude specified fields
20
+ .populate({
21
+ path: "jobSkillIds",
22
+ model: SkillModel,
23
+ select: "skillName",
24
+ })
25
+ .populate({
26
+ path: "jobPreferredSkillIds",
27
+ model: SkillModel,
28
+ select: "skillName",
29
+ })
30
+ .populate({
31
+ path: "jobRoleIds",
32
+ model: RoleModel,
33
+ select: "roleName",
34
+ })
35
+ .populate({
36
+ path: "organizationId",
37
+ model: ActiveOrganizationModel,
38
+ select: "organizationName",
39
+ })
40
+ .populate({
41
+ path: "qualificationIds",
42
+ model: EducationModel,
43
+ select: "label",
44
+ })
45
+ .populate({
46
+ path: "courseIds",
47
+ model: CourseModel,
48
+ select: "label",
49
+ })
50
+ .populate({
51
+ path: "specializationIds",
52
+ model: SpecializationModel,
53
+ select: "label",
54
+ });
55
+ };
56
+
57
+ module.exports = { populateJobPostDetails };
@@ -0,0 +1,40 @@
1
+ const nodemailer = require("nodemailer");
2
+
3
+ const sendEmail = async (senderEmail, emailBody) => {
4
+ // Check if the required environment variables are set
5
+ if (!process.env.EMAIL || !process.env.EMAIL_PASSWORD) {
6
+ const errorMsg = "Email credentials are not set in environment variables.";
7
+ console.error(errorMsg);
8
+ throw new Error(errorMsg); // Throw error to propagate it back to the caller
9
+ }
10
+
11
+ // Create a transporter using your email credentials
12
+ const transporter = nodemailer.createTransport({
13
+ service: "gmail",
14
+ auth: {
15
+ user: process.env.EMAIL,
16
+ pass: process.env.EMAIL_PASSWORD, // SMTP Password
17
+ },
18
+ });
19
+
20
+ // Email options
21
+ const mailOptions = {
22
+ from: process.env.EMAIL,
23
+ to: senderEmail,
24
+ subject: "Welcome to our OA Job Portal!",
25
+ html: emailBody,
26
+ };
27
+
28
+ // Send the email
29
+ try {
30
+ const info = await transporter.sendMail(mailOptions);
31
+ console.log(`Email sent successfully to ${senderEmail}: ${info.response}`);
32
+ // return { success: true, message: `Email sent to ${senderEmail}` }; // Return a success message
33
+ } catch (error) {
34
+ const errorMsg = `Error sending email to ${senderEmail}: ${error.message}`;
35
+ console.error(errorMsg);
36
+ throw new Error(errorMsg); // Throw error to propagate it back to the caller
37
+ }
38
+ };
39
+
40
+ module.exports = { sendEmail };