Initial commit - all project files via Git LFS

This commit is contained in:
2025-12-29 13:49:47 +05:30
commit e9c52271c6
80 changed files with 2713 additions and 0 deletions

2
.env Normal file
View File

@@ -0,0 +1,2 @@
EMAIL_USER=laxmibamnale2002@gmail.com
EMAIL_PASS=smqcwjwdsuiywrse

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
*.pdf filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text

3
contacts.json Normal file
View File

@@ -0,0 +1,3 @@
[
]

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6151d682778ae9a8794065adc611381fda940841aacbc1aa9aeb6d5dea51ffea
size 771090

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c498106d312c3e6df6b7cee5575dc4c535bc6f2c965dc7338abf93fc16863125
size 945980

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aebbabebc80ebf16465b20f24b3ce88153c6c9d8fbb0b042cb492d2ad959a185
size 387688

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c6756c2a1f395f71f0bea7b12fec08f0e6c39a74e4ce41beac8e40bd32cdd71d
size 653272

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:00f9b20bb8f70db9d7ccd87a223729c8bfca846f8def0e3084448b0a0a564734
size 203639

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0cef97032f9e432b0d8f9043b05f34142f2b2e6af88a9bf2c12d09a1b4497bd4
size 356409

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:669c0ab89af15f1b9b026ba3f74c8a4b74eeda131f1652fa319c914275d71656
size 270386

67
gallery.json Normal file
View File

@@ -0,0 +1,67 @@
[
{
"id": 1755343085723,
"category": "Awards & Achievements",
"caption": "MKVDC Pune Sanmanpatra dated",
"date": "",
"type": "image",
"url": "/gallery-media/1755343085623-cert1.jpg",
"createdAt": "2025-08-16T11:18:05.723Z"
},
{
"id": 1755343138070,
"category": "Awards & Achievements",
"caption": "Ambarnath - Kulgaon Badlapur Barrage certificate",
"date": "",
"type": "image",
"url": "/gallery-media/1755343137920-cert2.jpg",
"createdAt": "2025-08-16T11:18:58.070Z"
},
{
"id": 1755343165414,
"category": "Awards & Achievements",
"caption": "American Water Works Association Certificate",
"date": "",
"type": "image",
"url": "/gallery-media/1755343165376-cert3.jpg",
"createdAt": "2025-08-16T11:19:25.414Z"
},
{
"id": 1755343199851,
"category": "Awards & Achievements",
"caption": "Kerala Water Authority - Letter of Appreciation",
"date": "",
"type": "image",
"url": "/gallery-media/1755343199753-cert4.jpg",
"createdAt": "2025-08-16T11:19:59.851Z"
},
{
"id": 1755343250163,
"category": "Awards & Achievements",
"caption": "wilo ecolution award 2024",
"date": "",
"type": "image",
"url": "/gallery-media/1755343250133-WhatsApp Image 2025-08-09 at 12.17.33 PM.jpeg",
"createdAt": "2025-08-16T11:20:50.163Z",
"updatedAt": "2025-12-16T09:05:46.425Z"
},
{
"id": 1755764880336,
"category": "Awards & Achievements",
"caption": "Smart City Award for Western Zone, Solapur Automation",
"date": "",
"type": "image",
"url": "/gallery-media/1755764880241-WhatsApp Image 2023-09-27 at 5.04.05 PM.jpeg",
"createdAt": "2025-08-21T08:28:00.336Z",
"updatedAt": "2025-12-16T09:05:56.792Z"
},
{
"id": 1762603729920,
"category": "Awards & Achievements",
"caption": "CERTIFICATE OF APPRECIATION from HON. CHIEF MINISTER OF GOA for JJM Work successfully completed and commissioned",
"date": "",
"type": "image",
"url": "/gallery-media/1762603729917-CERTIFICATE OF APPRECIATION from HON. CHIEF MINISTER OF GOA for JJM Work successfully completed and commissioned.png",
"createdAt": "2025-11-08T12:08:49.920Z"
}
]

50
jobs.json Normal file
View File

@@ -0,0 +1,50 @@
[
{
"id": 1766485365354,
"positionName": "Software Developer",
"qualification": "BTech",
"experience": "1",
"location": "Pune",
"skills": [
"Python",
"java"
],
"numberOfOpenings": "2",
"jobDescription": "test",
"postingDate": "2025-12-23",
"closingDate": null,
"isActive": true
},
{
"id": 1766486098759,
"positionName": "Test",
"qualification": "BTech",
"experience": "1",
"location": "Pune",
"skills": [
"Python",
"java"
],
"numberOfOpenings": "2",
"jobDescription": "test",
"postingDate": "2025-12-23",
"closingDate": null,
"isActive": true
},
{
"id": 1766486114343,
"positionName": "Software Developer",
"qualification": "BTech",
"experience": "1",
"location": "Pune",
"skills": [
"Python",
"java"
],
"numberOfOpenings": "1",
"jobDescription": "test",
"postingDate": "2025-12-23",
"closingDate": null,
"isActive": true
}
]

1430
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "lcepl-backend",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"dev": "nodemon server.js"
},
"dependencies": {
"body-parser": "^1.20.3",
"cors": "^2.8.5",
"dotenv": "^17.2.1",
"express": "^4.21.2",
"fs": "^0.0.1-security",
"multer": "^2.0.2",
"nodemailer": "^7.0.5"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}

162
projects.json Normal file
View File

@@ -0,0 +1,162 @@
[
{
"id": 1754996200535,
"sector": "electromechanical",
"image": "/uploads/1754996200514-e811b15b-9d53-4c92-b2b0-280c724bec3b.jpg"
},
{
"id": 1754996228903,
"sector": "renewable energy",
"image": "/uploads/1754996228890-2a9cc942-939e-4b2a-8ed8-6fdb99cbe919.jpg"
},
{
"id": 1754996248901,
"sector": "roads",
"image": "/uploads/1754996248875-0e10027f-7b22-46c3-82d0-fd25ed88cd37.jpg"
},
{
"id": 1754996269954,
"sector": "storm water management",
"image": "/uploads/1754996269898-WhatsApp Image 2025-08-12 at 12.53.21 PM.jpeg"
},
{
"id": 1754996282054,
"sector": "storm water management",
"image": "/uploads/1754996282022-WhatsApp Image 2025-08-12 at 12.53.21 PM (1).jpeg"
},
{
"id": 1754996302879,
"sector": "tunnel",
"image": "/uploads/1754996302853-WhatsApp Image 2025-08-12 at 12.53.15 PM (1).jpeg"
},
{
"id": 1755338571673,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755338571628-Sewage-WT1.c1c508759572132379bf (1).jpg"
},
{
"id": 1755338632351,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755338632341-Non-conventional-water-treatment-plants-.e452887dc76a9c77aa01.jpg"
},
{
"id": 1755339600178,
"sector": "water supply",
"image": "/uploads/1755339600135-water_treatment_plan_QDiP2.e7a8e82ec7e3a4e3c148.jpg"
},
{
"id": 1755511932847,
"sector": "tunnel",
"image": "/uploads/1755511932814-WhatsApp Image 2025-08-18 at 2.24.17 PM.jpeg"
},
{
"id": 1755670485719,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755670485689-Picture1.jpg"
},
{
"id": 1755670494520,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755670494409-Picture2.png"
},
{
"id": 1755670504975,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755670504954-Picture3.jpg"
},
{
"id": 1755673960257,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755673960238-Picture4.png"
},
{
"id": 1755673968617,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755673968590-Picture5.png"
},
{
"id": 1755673977458,
"sector": "wastewater / sewerage works",
"image": "/uploads/1755673977434-Picture6.png"
},
{
"id": 1755674126735,
"sector": "electromechanical",
"image": "/uploads/1755674126708-image (2).jpg"
},
{
"id": 1755764796849,
"sector": "real estate / buildings",
"image": "/uploads/1755764796583-04da8a50-98a3-4516-86de-e8f8f46594b8.jpg"
},
{
"id": 1762768071860,
"sector": "real estate / buildings",
"image": "/uploads/1762768071841-Gemini_Generated_Image_l7v50ml7v50ml7v5.png"
},
{
"id": 1762768163950,
"sector": "wastewater / sewerage works",
"image": "/uploads/1762768163944-WhatsApp Image 2025-11-10 at 3.18.33 PM.jpeg"
},
{
"id": 1766058099201,
"sector": "water supply",
"image": "/uploads/1766058099192-Photo 40.jpeg"
},
{
"id": 1766058106964,
"sector": "water supply",
"image": "/uploads/1766058106951-Photo 39.jpeg"
},
{
"id": 1766058118717,
"sector": "water supply",
"image": "/uploads/1766058118709-Photo 28.jpeg"
},
{
"id": 1766058179446,
"sector": "electromechanical-instrumentation",
"image": "/uploads/1766058179433-WhatsApp Image 2025-12-17 at 11.14.20 AM.jpeg"
},
{
"id": 1766058376010,
"sector": "wastewater / sewerage",
"image": "/uploads/1766058376000-WhatsApp Image 2025-12-17 at 6.56.08 PM.jpeg"
},
{
"id": 1766058429912,
"sector": "wastewater / sewerage",
"image": "/uploads/1766058429903-WhatsApp Image 2025-12-17 at 10.23.09 AM (2).jpeg"
},
{
"id": 1766058519500,
"sector": "wastewater / sewerage",
"image": "/uploads/1766058519485-WhatsApp Image 2025-12-17 at 10.23.00 AM.jpeg"
},
{
"id": 1766058540827,
"sector": "roads",
"image": "/uploads/1766058540797-WhatsApp Image 2025-12-17 at 5.44.30 PM.jpeg"
},
{
"id": 1766137181427,
"sector": "electromechanical & instrumentation",
"image": "/uploads/1766137181418-WhatsApp Image 2025-12-17 at 11.14.21 AM.jpeg"
},
{
"id": 1766137224619,
"sector": "electromechanical & instrumentation",
"image": "/uploads/1766137224615-electro.png"
},
{
"id": 1766137282049,
"sector": "storm water",
"image": "/uploads/1766137282043-WhatsApp Image 2025-12-17 at 1.16.29 PM (1).jpeg"
},
{
"id": 1766137297598,
"sector": "storm water",
"image": "/uploads/1766137297588-WhatsApp Image 2025-12-17 at 1.16.29 PM.jpeg"
}
]

8
routes/contactRoutes.js Normal file
View File

@@ -0,0 +1,8 @@
const express = require('express');
const router = express.Router();
const { saveContact } = require('../controllers/contactController');
// ✅ Use `router.post`, NOT `app.post`
router.post('/', saveContact);
module.exports = router;

744
server.js Normal file
View File

@@ -0,0 +1,744 @@
const express = require("express");
const cors = require("cors");
const nodemailer = require("nodemailer");
const multer = require("multer");
const fs = require("fs");
const path = require("path");
const app = express();
const PORT = 8000;
const DATA_FILE = path.join(__dirname, "projects.json");
const JOBS_DATA_FILE = path.join(__dirname, "jobs.json");
const GALLERY_DATA_FILE = path.join(__dirname, "gallery.json");
app.use(cors());
app.use(express.json());
app.use("/uploads", express.static(path.join(__dirname, "uploads")));
app.use("/applications", express.static(path.join(__dirname, "applications")));
app.use("/gallery-media", express.static(path.join(__dirname, "gallery-media")));
const loadData = (filePath) => {
if (!fs.existsSync(filePath)) return [];
const data = fs.readFileSync(filePath, "utf-8");
try {
return JSON.parse(data);
} catch {
return [];
}
};
const saveData = (filePath, data) => {
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
};
// ====================== GALLERY API ====================== //
const galleryStorage = multer.diskStorage({
destination: (req, file, cb) => {
const dir = "./gallery-media";
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
cb(null, dir);
},
filename: (req, file, cb) => {
const uniqueName = Date.now() + "-" + file.originalname;
cb(null, uniqueName);
},
});
const uploadGallery = multer({
storage: galleryStorage,
fileFilter: (req, file, cb) => {
const filetypes = /jpeg|jpg|png|gif|mp4|mov|avi/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if (extname && mimetype) {
return cb(null, true);
} else {
cb(new Error("Only images (JPEG, JPG, PNG, GIF) and videos (MP4, MOV, AVI) are allowed"));
}
},
limits: { fileSize: 500 * 1024 * 1024 } // 500MB limit
});
// Get all gallery items with filtering by category
app.get("/api/gallery", (req, res) => {
const items = loadData(GALLERY_DATA_FILE);
if (req.query.category) {
const filtered = items.filter(item => item.category === req.query.category);
return res.json(filtered);
}
res.json(items);
});
// Add new gallery item
app.post("/api/gallery", uploadGallery.single("media"), (req, res) => {
const { category, caption, date } = req.body;
const mediaFile = req.file;
if (!category || !mediaFile) {
return res.status(400).json({ error: "Category and media file are required" });
}
const mediaType = mediaFile.mimetype.startsWith("video") ? "video" : "image";
const newItem = {
id: Date.now(),
category,
caption: caption || "",
date: date || "",
type: mediaType,
url: `/gallery-media/${mediaFile.filename}`,
createdAt: new Date().toISOString()
};
const items = loadData(GALLERY_DATA_FILE);
items.push(newItem);
saveData(GALLERY_DATA_FILE, items);
res.status(201).json({ message: "Gallery item added successfully", item: newItem });
});
// Update gallery item
app.put("/api/gallery/:id", uploadGallery.single("media"), (req, res) => {
const itemId = parseInt(req.params.id);
const { category, caption, date } = req.body;
const mediaFile = req.file;
let items = loadData(GALLERY_DATA_FILE);
const itemIndex = items.findIndex(item => item.id === itemId);
if (itemIndex === -1) {
return res.status(404).json({ error: "Gallery item not found" });
}
const existingItem = items[itemIndex];
let mediaType = existingItem.type;
let mediaUrl = existingItem.url;
if (mediaFile) {
// Delete old media file
const oldFilename = existingItem.url.split("/").pop();
const oldPath = path.join(__dirname, "gallery-media", oldFilename);
if (fs.existsSync(oldPath)) {
fs.unlinkSync(oldPath);
}
mediaType = mediaFile.mimetype.startsWith("video") ? "video" : "image";
mediaUrl = `/gallery-media/${mediaFile.filename}`;
}
items[itemIndex] = {
...existingItem,
category: category || existingItem.category,
caption: caption || existingItem.caption,
date: date || existingItem.date,
type: mediaType,
url: mediaUrl,
updatedAt: new Date().toISOString()
};
saveData(GALLERY_DATA_FILE, items);
res.json({ message: "Gallery item updated successfully", item: items[itemIndex] });
});
// Delete gallery item
app.delete("/api/gallery/:id", (req, res) => {
const itemId = parseInt(req.params.id);
const items = loadData(GALLERY_DATA_FILE);
const itemIndex = items.findIndex(item => item.id === itemId);
if (itemIndex === -1) {
return res.status(404).json({ error: "Gallery item not found" });
}
// Delete associated media file
const filename = items[itemIndex].url.split("/").pop();
const filePath = path.join(__dirname, "gallery-media", filename);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
items.splice(itemIndex, 1);
saveData(GALLERY_DATA_FILE, items);
res.json({ message: "Gallery item deleted successfully" });
});
// ========= CONTACT FORM ========= //
app.post("/contact", async (req, res) => {
const { name, email, contact, message } = req.body;
// Validation
if (!name || !email || !contact || !message) {
return res.status(400).json({ success: false, error: "All fields are required" });
}
// Additional validation
if (name.length < 2) {
return res.status(400).json({ success: false, error: "Name must be at least 2 characters" });
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
return res.status(400).json({ success: false, error: "Invalid email format" });
}
const digits = contact.replace(/\D/g, "");
if (digits.length < 7) {
return res.status(400).json({ success: false, error: "Contact number must have at least 7 digits" });
}
if (message.length < 10) {
return res.status(400).json({ success: false, error: "Message must be at least 10 characters" });
}
try {
// Use environment variables for email credentials
const emailUser = process.env.EMAIL_USER || "laxmibamnale2002@gmail.com";
const emailPass = process.env.EMAIL_PASS || "smqcwjwdsuiywrse";
if (!emailPass) {
console.error("Email password not configured");
return res.status(500).json({ success: false, message: "Server configuration error" });
}
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: emailUser,
pass: emailPass,
},
});
// Verify connection configuration
await transporter.verify();
const mailToOwner = {
from: emailUser, // Use your email as from address to avoid authentication issues
replyTo: email, // Set reply-to to customer's email
to: emailUser,
subject: `New Contact Form Submission from ${name}`,
html: `
<h3>New Contact Form Submission</h3>
<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Contact:</strong> ${contact}</p>
<p><strong>Message:</strong></p>
<p>${message.replace(/\n/g, "<br>")}</p>
<p><em>Received on: ${new Date().toLocaleString()}</em></p>
`,
};
await transporter.sendMail(mailToOwner);
const autoReply = {
from: emailUser,
to: email,
subject: "Thank you for contacting Laxmi Civil Engineering Services!",
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #0056b3;">Thank You for Contacting Us!</h2>
<p>Dear ${name},</p>
<p>Thank you for reaching out to Laxmi Civil Engineering Services Pvt. Ltd. We have received your message and will get back to you within 24-48 hours.</p>
<p>For urgent inquiries, please call us at <strong>0231-2521554</strong> or <strong>0231-2683900</strong>.</p>
<p><strong>Summary of your message:</strong></p>
<blockquote style="background: #f9f9f9; padding: 10px; border-left: 4px solid #ccc;">
${message.replace(/\n/g, "<br>")}
</blockquote>
<p>Best regards,<br/>Team Laxmi Civil Engineering Services</p>
<hr style="border: none; border-top: 1px solid #eee; margin: 20px 0;">
<p style="font-size: 12px; color: #777;">
Laxmi Civil Engineering Services Pvt. Ltd.<br>
1148, E. Sykes Extension, Kolhapur 416 001, Maharashtra, India.<br>
Phone: 0231-2521554, 2683900 | Email: laxmibamnale2002@gmail.com
</p>
</div>
`,
};
await transporter.sendMail(autoReply);
res.status(200).json({ success: true, message: "Message sent successfully" });
} catch (err) {
console.error("Error sending email:", err);
if (err.code === "EAUTH") {
res.status(500).json({ success: false, message: "Email authentication failed" });
} else if (err.code === "EENVELOPE") {
res.status(400).json({ success: false, message: "Invalid email address" });
} else {
res.status(500).json({ success: false, message: "Server error while sending email" });
}
}
});
// ========= PROJECT UPLOAD ========= //
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const dir = "./uploads";
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
cb(null, dir);
},
filename: (req, file, cb) => {
const uniqueName = Date.now() + "-" + file.originalname;
cb(null, uniqueName);
},
});
const upload = multer({ storage });
const loadProjects = () => {
if (!fs.existsSync(DATA_FILE)) return [];
const data = fs.readFileSync(DATA_FILE, "utf-8");
try {
return JSON.parse(data);
} catch {
return [];
}
};
const saveProjects = (projects) => {
fs.writeFileSync(DATA_FILE, JSON.stringify(projects, null, 2));
};
app.post("/api/projects", upload.single("image"), (req, res) => {
const { sector } = req.body;
const image = req.file ? `/uploads/${req.file.filename}` : "";
if (!sector || !image) {
return res.status(400).json({ error: "Sector and image are required" });
}
const newProject = {
id: Date.now(),
sector,
image,
};
const projects = loadProjects();
projects.push(newProject);
saveProjects(projects);
res.status(201).json({ message: "Project added successfully", project: newProject });
});
app.get("/api/projects", (req, res) => {
const projects = loadProjects();
res.json(projects);
});
app.post("/api/projects/update/:id", upload.single("image"), (req, res) => {
const projectId = parseInt(req.params.id);
const { sector } = req.body;
let projects = loadProjects();
const projectIndex = projects.findIndex((p) => p.id === projectId);
if (projectIndex === -1) {
return res.status(404).json({ error: "Project not found" });
}
const existingProject = projects[projectIndex];
projects[projectIndex] = {
...existingProject,
sector,
image: req.file ? `/uploads/${req.file.filename}` : existingProject.image,
};
saveProjects(projects);
res.json({ message: "Project updated successfully", project: projects[projectIndex] });
});
app.delete("/api/projects/:id", (req, res) => {
const projectId = parseInt(req.params.id);
let projects = loadProjects();
const updatedProjects = projects.filter((project) => project.id !== projectId);
if (projects.length === updatedProjects.length) {
return res.status(404).json({ error: "Project not found" });
}
saveProjects(updatedProjects);
res.json({ message: "Project deleted successfully" });
});
// ========= JOB APPLICATION UPLOAD ========= //
const applicationStorage = multer.diskStorage({
destination: (req, file, cb) => {
const dir = "./applications";
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
cb(null, dir);
},
filename: (req, file, cb) => {
const uniqueName = Date.now() + "-" + file.originalname;
cb(null, uniqueName);
},
});
const uploadApplication = multer({ storage: applicationStorage });
app.post("/send-application", uploadApplication.single("resume"), async (req, res) => {
const {
fullName,
email,
phone,
address,
education,
skill,
interest,
totalExperience,
expectedSalary,
currentCompany,
currentDesignation,
} = req.body;
if (!fullName || !email || !phone || !education || !skill || !totalExperience || !req.file) {
return res.status(400).json({ success: false, message: "Missing required fields" });
}
const resumePath = path.join(__dirname, "applications", req.file.filename);
try {
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "laxmibamnale2002@gmail.com",
pass: "smqcwjwdsuiywrse",
},
});
const mailOptions = {
from: email,
to: "laxmibamnale2002@gmail.com",
subject: `New Job Application from ${fullName}`,
text: `Full Name: ${fullName}
Email: ${email}
Phone: ${phone}
Address: ${address}
Education: ${education}
Skill: ${skill}
Interest: ${interest}
Experience: ${totalExperience}
Expected Salary: ${expectedSalary}
Current Company: ${currentCompany}
Current Designation: ${currentDesignation}`,
attachments: [
{
filename: req.file.originalname,
path: resumePath,
},
],
};
await transporter.sendMail(mailOptions);
const autoReply = {
from: "laxmibamnale2002@gmail.com",
to: email,
subject: "Application Received - Laxmi Civil Engineering Services",
text: `Dear ${fullName},
Thank you for applying to Laxmi Civil Engineering Services Pvt. Ltd.
We have received your application and resume. Our HR team will review your profile and get back to you shortly if shortlisted.
Best regards,
Laxmi Civil Engineering Services Pvt. Ltd.`,
};
await transporter.sendMail(autoReply);
res.status(200).json({ success: true, message: "Application submitted successfully" });
} catch (err) {
console.error("Error submitting application:", err);
res.status(500).json({ success: false, message: "Server error" });
}
});
// ========= ENHANCED JOB POSTINGS API ========= //
const loadJobs = () => {
if (!fs.existsSync(JOBS_DATA_FILE)) return [];
const data = fs.readFileSync(JOBS_DATA_FILE, "utf-8");
try {
return JSON.parse(data);
} catch {
return [];
}
};
const saveJobs = (jobs) => {
fs.writeFileSync(JOBS_DATA_FILE, JSON.stringify(jobs, null, 2));
};
// Get all jobs with automatic status updates
app.get("/api/jobs", (req, res) => {
let jobs = loadJobs();
const currentDate = new Date();
// Update job statuses based on closing date
jobs = jobs.map(job => {
if (job.closingDate && new Date(job.closingDate) < currentDate) {
return { ...job, isActive: false };
}
return job;
});
// Save updated statuses if any changed
saveJobs(jobs);
res.json(jobs);
});
// Create new job posting with enhanced fields
app.post("/api/jobs", (req, res) => {
const {
positionName,
qualification,
experience,
location,
skills,
numberOfOpenings,
jobDescription,
postingDate,
closingDate,
isActive = true
} = req.body;
if (
!positionName ||
!qualification ||
!experience ||
!location ||
!Array.isArray(skills) ||
skills.length === 0 ||
!numberOfOpenings ||
!jobDescription ||
!postingDate
) {
return res.status(400).json({ error: "Missing required fields" });
}
const jobs = loadJobs();
const newJob = {
id: Date.now(),
positionName,
qualification,
experience,
location,
skills,
numberOfOpenings,
jobDescription,
postingDate,
closingDate: closingDate || null,
isActive: closingDate ? new Date(closingDate) >= new Date() : isActive
};
jobs.push(newJob);
saveJobs(jobs);
res.status(201).json({ message: "Job created", job: newJob });
});
// Update existing job posting with enhanced fields
app.put("/api/jobs/:id", (req, res) => {
const jobId = parseInt(req.params.id);
const {
positionName,
qualification,
experience,
location,
skills,
numberOfOpenings,
jobDescription,
postingDate,
closingDate,
isActive
} = req.body;
const jobs = loadJobs();
const index = jobs.findIndex((job) => job.id === jobId);
if (index === -1) {
return res.status(404).json({ error: "Job not found" });
}
if (
!positionName ||
!qualification ||
!experience ||
!location ||
!Array.isArray(skills) ||
skills.length === 0 ||
!numberOfOpenings ||
!jobDescription ||
!postingDate
) {
return res.status(400).json({ error: "Missing required fields" });
}
const currentDate = new Date();
const closingDateObj = closingDate ? new Date(closingDate) : null;
const actualIsActive = closingDateObj ? closingDateObj >= currentDate : isActive;
jobs[index] = {
id: jobId,
positionName,
qualification,
experience,
location,
skills,
numberOfOpenings,
jobDescription,
postingDate,
closingDate: closingDate || null,
isActive: actualIsActive
};
saveJobs(jobs);
res.json({ message: "Job updated", job: jobs[index] });
});
// Toggle job active status
app.patch("/api/jobs/:id/toggle-active", (req, res) => {
const jobId = parseInt(req.params.id);
const jobs = loadJobs();
const index = jobs.findIndex((job) => job.id === jobId);
if (index === -1) {
return res.status(404).json({ error: "Job not found" });
}
// Don't allow toggling if there's a closing date in the past
const currentDate = new Date();
if (jobs[index].closingDate && new Date(jobs[index].closingDate) < currentDate) {
return res.status(400).json({ error: "Cannot activate job with expired closing date" });
}
jobs[index].isActive = !jobs[index].isActive;
saveJobs(jobs);
res.json({ message: "Job status updated", job: jobs[index] });
});
// Delete job posting
app.delete("/api/jobs/:id", (req, res) => {
const jobId = parseInt(req.params.id);
let jobs = loadJobs();
const initialLength = jobs.length;
jobs = jobs.filter((job) => job.id !== jobId);
if (jobs.length === initialLength) {
return res.status(404).json({ error: "Job not found" });
}
saveJobs(jobs);
res.json({ message: "Job deleted" });
});
// ========= CAREER CONTACT FORM ========= //
const careerStorage = multer.diskStorage({
destination: (req, file, cb) => {
const dir = "./career-applications";
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
cb(null, dir);
},
filename: (req, file, cb) => {
const uniqueName = Date.now() + "-" + file.originalname;
cb(null, uniqueName);
},
});
const uploadCareer = multer({
storage: careerStorage,
limits: { fileSize: 10 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
const allowedExt = ['.pdf', '.doc', '.docx'];
const ext = path.extname(file.originalname).toLowerCase();
if (allowedExt.includes(ext)) {
cb(null, true);
} else {
cb(new Error('Only PDF/DOC/DOCX files are allowed'));
}
}
});
app.post('/api/careers/contact', uploadCareer.single('resume'), async (req, res) => {
try {
const { fullName, email, phone } = req.body;
const resumeFile = req.file;
if (!fullName || !email || !resumeFile) {
return res.status(400).json({ success: false, error: 'Missing required fields or resume.' });
}
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "laxmibamnale2002@gmail.com",
pass: "smqcwjwdsuiywrse",
},
});
const mailOptions = {
from: '"Career Contact Form" <laxmibamnale2002@gmail.com>',
to: "laxmibamnale2002@gmail.com",
subject: `New Career Contact from ${fullName}`,
text: `
You have received a new career contact form submission.
Full Name: ${fullName}
Email: ${email}
Phone: ${phone || 'Not provided'}
Resume is attached.
`,
attachments: [
{
filename: resumeFile.originalname,
path: resumeFile.path,
},
],
};
await transporter.sendMail(mailOptions);
const autoReply = {
from: "<laxmibamnale2002@gmail.com>",
to: email,
subject: "Thank you for contacting Laxmi Civil Engineering Services",
text: `Dear ${fullName},
Thank you for reaching out to us via the Career Contact form. We have received your details and resume.
Our HR team will review and get back to you soon.
Best regards,
Laxmi Civil Engineering Services Pvt. Ltd.`,
};
await transporter.sendMail(autoReply);
fs.unlink(resumeFile.path, (err) => {
if (err) console.error('Error deleting uploaded resume:', err);
});
res.json({ success: true, message: 'Career contact form submitted successfully.' });
} catch (error) {
console.error('Error in /api/careers/contact:', error);
res.status(500).json({ success: false, message: 'Server error' });
}
});
// ========= START SERVER ========= //
app.listen(PORT, "0.0.0.0", () => {
console.log(`Server running on http://0.0.0.0:${PORT}`);
});

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b18868133b335ea2090a0f979a237ffbc55e126cd838e6674e55c079ff211f01
size 136480

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d7f81aa3c405fe8a1530a32162e46e70cecb6879c45886b73ab5470805c87d0d
size 95483

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:971601aca01935b4252c71bc6046a190e9ce363e1fdcb3e0c3191f98151e3d0c
size 117116

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:89854e5ffb6247fdbc2daa425eaa07582259841044f32c8a3b82190f9a01dbc2
size 106340

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:06ead77138dc9b9e39fbb4f9a4e1f89e88a974659017c89a0a1cdc2a8d401639
size 40215

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aa1a201f199ad52ff266d1f7d86cd52b40a268b5c067e56d8e9bb312d8f78d78
size 77916

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f9f44f85a7fe7847a32aeead56bcf5a183e2b96726cf7c18b1ca982c833bce07
size 41260

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:353a56addc86cf52057f96d712a1e9c284603e48cb6659b270d1795d9d844a65
size 217645

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa95b026a5572e1981a34fa471c0db1ef5f7d5cd2a70ae3126eefe04877ba577
size 270148

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0969ee184ba4d08fbfe22b0b2a9d49dffca13430bf3b0b4cccbc0f278801c4c8
size 266050

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:545b067a2f79fe311739ee81b5e1996e7b39ee0a487c69195e73036fe7120e25
size 388066

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:53a0edd4dc4098523d53726512853e22ef552d745f4aed4522e3eff07c82ab2b
size 184279

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc28513a19968b279285647f01ba195d63c469671f273639ad18fd416514d483
size 136512

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4157cf889539b71cfdb6d071cb7a770aac8f048294010032b250c25e51fe0e92
size 74954

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be0e72ab911a5b5d3e38ef303fe3e12577aa2375c4a678b29638c7d87b8be425
size 30905

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c0d1472f857f577deccbb5238db41ce83c70725e59a27852ced86e38aba96972
size 490294

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f42ff8a40a2029248dbbf537bc61f808bccff06433495f401010489d54bb870c
size 33340

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5ebf1121ad3b28d5f203e08af0053c081cae1d16a0f3d944d440ec0b9dae02c9
size 79635

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:016a9b7de9e0c53a62eceae8dccda11b580e86b1e04737cebc05593503c39ad3
size 68559

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7bfd53cb52febcecef8fdbf6b271fd96ce6e9f52fd273e2535a93034b46df764
size 574646

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:af819c90ab29c7bf2577e804bddd74f449be8359925eaf1548835016896750cb
size 93085

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a2218e865746cf08c666663e772e748e54566f668d728dc0993a5a502a3f9a99
size 158030

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:911178bbdf45d142ff44f5ee2908f868027aa812ac5eaf436b62d7efada1c412
size 115016

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d5bb053a2b168271db97b5dd825c39b2df1a49dc340bccf23f0a76e302f7f0ea
size 274756

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0dfc6b5fd10587629e067afdc9086503e3086b0cb88be4546e2824cfef3c4e66
size 229604

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34927bb18c3b47e65ed4ff8fd0883aee7c54915709b6e4433b746c38ed851090
size 145817

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:de82b69aabc58848077a790ed8c4874c5398a42118990b2d0c5bf6017ad10816
size 772511

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9bb99fc68316b0c51ea7ddaf06f4a60347bf3a3aa4b1029c633d0d29562d0cae
size 118059

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f0fc2085eaac95b19d87dbb716d6880101679e0d60820aa5357e4a21d674f5c
size 248825

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c538761aa7c28c070da0a18cdfaecc38b88638064a78126883fcd637c496f44c
size 291194

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3033c81e52b8bd1d8fef279fee695a7abc4aa01af5515c58691c2cb2c063e161
size 238091

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f0509d8451f0de35d5e2b8d2fcafde5d918989fe4a05c456cef036d432a162da
size 265880

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fbfc91893f43f579745a167c8271e99c15bc859d0542cc0cc1c6e945429411e1
size 81411

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6604eaa800b5b64e97a7e5a7e5c77ff13927455692cc69070ccdcd12dff2c5e1
size 341134

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:920ceaebc2f90401cee2d5b0c26c4fc2f9beada7d9a17dd90843a472111c2eed
size 150356

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e181286a94a43674cc5f91f53dc7993a0b35deabc1676b76f6a95d2a82f09dd6
size 1296332

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e4647cc03eb202cd888fa41ba400e9ff4bfcc8b563280cfbf3035f2fb0fb3893
size 1308696

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8652fc37c2ca1cd4d186e61d5dcaf0528dea22cbc0026704a195f8f091678d4d
size 1399820

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ff2674a0961bce76365d05311048c2866bbfb8e8d0059ede2f5484302eae3be9
size 1643635

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ff2674a0961bce76365d05311048c2866bbfb8e8d0059ede2f5484302eae3be9
size 1643635

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be3d05534aa1434da4744a377e3d2cfd5360d075e7d23a3405812dd62794959d
size 258388

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:722ac9b0e5c4ac59a7e098ac380b99bd28b8983a994ce66ab836f3a4ddb5b3a4
size 220027

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9d2d6b89d56c55b32fb1fb11f3dc4300e8a53882be08579782ce5866bb2a80a8
size 163622

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:49dda7fbecd085642f0f78e9c5d28730cf0a9cdb44098e9a651cbefcc68c503e
size 312187

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3d0704c9283aea311467fed713cf5bec5b1a62c501842a5054ecee148588d25b
size 143013

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a4a3fff41cc800142586b213149acbc460d9020c3a26c8c5a52ba27b310e07ad
size 198794

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6a8da8fa8dd2242160f7a82f0e84fa50f940bb9bb98757d0af005d9fae7e2527
size 224299

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30430e08b449c6ef0a3191f76d763316957f931e60630e3e70112d1a8a20cabf
size 171577

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa95b026a5572e1981a34fa471c0db1ef5f7d5cd2a70ae3126eefe04877ba577
size 270148

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:71d73cc2e658f634c63e967e84f9040be5ba6fc1e8711d0aaad4689689b7d06d
size 244264

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d9a998d16ba7715aebef07321902b0dbf7d79e5e218c1ad4e98e37f7a82a98bc
size 195839

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:545b067a2f79fe311739ee81b5e1996e7b39ee0a487c69195e73036fe7120e25
size 388066

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:175b8d6158b2d800e0de03337810367e7d6f84eee5bffc1fab29449a9f2be425
size 196030

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2bc83397b5da254b5cde16bfb86012f150600d4cb1fc440519631fd80de47aaa
size 194412

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:38c83d7a6909fc5404684fdb3aeb53858447bc4a30194d1176c7c5cb73e3f1e7
size 155144

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e9196f24a766e07e8b3fb9e46a3a6e4b6ac583c2eb4521c50e9888bec2b5acd7
size 695000

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:267eca9db020fabcb0479b64ab51cd76c26a0afeac137b8c0dae08b0cb77354f
size 266099

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:670b1ab2113c16a332d369ace1c1bf5bd593d8410ebb3f420dee5687e4d459df
size 247663

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5ae818d036da6886c461772a64b4d1a0667937d178df2b3f8c3e2c54038a622a
size 376889

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:061fb64bacdab2875d69fb93d173ea9e37660ddd8bb5a61470cb80c7ba82056f
size 38986

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:353a56addc86cf52057f96d712a1e9c284603e48cb6659b270d1795d9d844a65
size 217645

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5b2c3909ccaf45150a8fdc370f6d345c1198151f6523d1016e2c8486e4ebdd6c
size 239400

15
utils/fileHandler.js Normal file
View File

@@ -0,0 +1,15 @@
const fs = require('fs').promises;
const path = require('path');
const dataPath = path.join(__dirname, '../data/applications.json');
const readApplications = async () => {
const data = await fs.readFile(dataPath, 'utf-8');
return JSON.parse(data);
};
const writeApplications = async (data) => {
await fs.writeFile(dataPath, JSON.stringify(data, null, 2), 'utf-8');
};
module.exports = { readApplications, writeApplications };