Initial commit - all project files via Git LFS
This commit is contained in:
2
.env
Normal file
2
.env
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
EMAIL_USER=laxmibamnale2002@gmail.com
|
||||||
|
EMAIL_PASS=smqcwjwdsuiywrse
|
||||||
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal 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
3
contacts.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[
|
||||||
|
|
||||||
|
]
|
||||||
3
gallery-media/1755343085623-cert1.jpg
Normal file
3
gallery-media/1755343085623-cert1.jpg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6151d682778ae9a8794065adc611381fda940841aacbc1aa9aeb6d5dea51ffea
|
||||||
|
size 771090
|
||||||
3
gallery-media/1755343137920-cert2.jpg
Normal file
3
gallery-media/1755343137920-cert2.jpg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:c498106d312c3e6df6b7cee5575dc4c535bc6f2c965dc7338abf93fc16863125
|
||||||
|
size 945980
|
||||||
3
gallery-media/1755343165376-cert3.jpg
Normal file
3
gallery-media/1755343165376-cert3.jpg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:aebbabebc80ebf16465b20f24b3ce88153c6c9d8fbb0b042cb492d2ad959a185
|
||||||
|
size 387688
|
||||||
3
gallery-media/1755343199753-cert4.jpg
Normal file
3
gallery-media/1755343199753-cert4.jpg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:c6756c2a1f395f71f0bea7b12fec08f0e6c39a74e4ce41beac8e40bd32cdd71d
|
||||||
|
size 653272
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:00f9b20bb8f70db9d7ccd87a223729c8bfca846f8def0e3084448b0a0a564734
|
||||||
|
size 203639
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0cef97032f9e432b0d8f9043b05f34142f2b2e6af88a9bf2c12d09a1b4497bd4
|
||||||
|
size 356409
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:669c0ab89af15f1b9b026ba3f74c8a4b74eeda131f1652fa319c914275d71656
|
||||||
|
size 270386
|
||||||
67
gallery.json
Normal file
67
gallery.json
Normal 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
50
jobs.json
Normal 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
1430
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal 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
162
projects.json
Normal 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
8
routes/contactRoutes.js
Normal 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
744
server.js
Normal 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}`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:b18868133b335ea2090a0f979a237ffbc55e126cd838e6674e55c079ff211f01
|
||||||
|
size 136480
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d7f81aa3c405fe8a1530a32162e46e70cecb6879c45886b73ab5470805c87d0d
|
||||||
|
size 95483
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:971601aca01935b4252c71bc6046a190e9ce363e1fdcb3e0c3191f98151e3d0c
|
||||||
|
size 117116
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:89854e5ffb6247fdbc2daa425eaa07582259841044f32c8a3b82190f9a01dbc2
|
||||||
|
size 106340
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:06ead77138dc9b9e39fbb4f9a4e1f89e88a974659017c89a0a1cdc2a8d401639
|
||||||
|
size 40215
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:aa1a201f199ad52ff266d1f7d86cd52b40a268b5c067e56d8e9bb312d8f78d78
|
||||||
|
size 77916
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f9f44f85a7fe7847a32aeead56bcf5a183e2b96726cf7c18b1ca982c833bce07
|
||||||
|
size 41260
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:353a56addc86cf52057f96d712a1e9c284603e48cb6659b270d1795d9d844a65
|
||||||
|
size 217645
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fa95b026a5572e1981a34fa471c0db1ef5f7d5cd2a70ae3126eefe04877ba577
|
||||||
|
size 270148
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0969ee184ba4d08fbfe22b0b2a9d49dffca13430bf3b0b4cccbc0f278801c4c8
|
||||||
|
size 266050
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:545b067a2f79fe311739ee81b5e1996e7b39ee0a487c69195e73036fe7120e25
|
||||||
|
size 388066
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:53a0edd4dc4098523d53726512853e22ef552d745f4aed4522e3eff07c82ab2b
|
||||||
|
size 184279
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:dc28513a19968b279285647f01ba195d63c469671f273639ad18fd416514d483
|
||||||
|
size 136512
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:4157cf889539b71cfdb6d071cb7a770aac8f048294010032b250c25e51fe0e92
|
||||||
|
size 74954
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:be0e72ab911a5b5d3e38ef303fe3e12577aa2375c4a678b29638c7d87b8be425
|
||||||
|
size 30905
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:c0d1472f857f577deccbb5238db41ce83c70725e59a27852ced86e38aba96972
|
||||||
|
size 490294
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f42ff8a40a2029248dbbf537bc61f808bccff06433495f401010489d54bb870c
|
||||||
|
size 33340
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5ebf1121ad3b28d5f203e08af0053c081cae1d16a0f3d944d440ec0b9dae02c9
|
||||||
|
size 79635
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:016a9b7de9e0c53a62eceae8dccda11b580e86b1e04737cebc05593503c39ad3
|
||||||
|
size 68559
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:7bfd53cb52febcecef8fdbf6b271fd96ce6e9f52fd273e2535a93034b46df764
|
||||||
|
size 574646
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:af819c90ab29c7bf2577e804bddd74f449be8359925eaf1548835016896750cb
|
||||||
|
size 93085
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:a2218e865746cf08c666663e772e748e54566f668d728dc0993a5a502a3f9a99
|
||||||
|
size 158030
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:911178bbdf45d142ff44f5ee2908f868027aa812ac5eaf436b62d7efada1c412
|
||||||
|
size 115016
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d5bb053a2b168271db97b5dd825c39b2df1a49dc340bccf23f0a76e302f7f0ea
|
||||||
|
size 274756
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0dfc6b5fd10587629e067afdc9086503e3086b0cb88be4546e2824cfef3c4e66
|
||||||
|
size 229604
|
||||||
3
uploads/1755670485689-Picture1.jpg
Normal file
3
uploads/1755670485689-Picture1.jpg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:34927bb18c3b47e65ed4ff8fd0883aee7c54915709b6e4433b746c38ed851090
|
||||||
|
size 145817
|
||||||
3
uploads/1755670494409-Picture2.png
Normal file
3
uploads/1755670494409-Picture2.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:de82b69aabc58848077a790ed8c4874c5398a42118990b2d0c5bf6017ad10816
|
||||||
|
size 772511
|
||||||
3
uploads/1755670504954-Picture3.jpg
Normal file
3
uploads/1755670504954-Picture3.jpg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9bb99fc68316b0c51ea7ddaf06f4a60347bf3a3aa4b1029c633d0d29562d0cae
|
||||||
|
size 118059
|
||||||
3
uploads/1755673960238-Picture4.png
Normal file
3
uploads/1755673960238-Picture4.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:7f0fc2085eaac95b19d87dbb716d6880101679e0d60820aa5357e4a21d674f5c
|
||||||
|
size 248825
|
||||||
3
uploads/1755673968590-Picture5.png
Normal file
3
uploads/1755673968590-Picture5.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:c538761aa7c28c070da0a18cdfaecc38b88638064a78126883fcd637c496f44c
|
||||||
|
size 291194
|
||||||
3
uploads/1755673977434-Picture6.png
Normal file
3
uploads/1755673977434-Picture6.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:3033c81e52b8bd1d8fef279fee695a7abc4aa01af5515c58691c2cb2c063e161
|
||||||
|
size 238091
|
||||||
3
uploads/1755674126708-image (2).jpg
Normal file
3
uploads/1755674126708-image (2).jpg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f0509d8451f0de35d5e2b8d2fcafde5d918989fe4a05c456cef036d432a162da
|
||||||
|
size 265880
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fbfc91893f43f579745a167c8271e99c15bc859d0542cc0cc1c6e945429411e1
|
||||||
|
size 81411
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6604eaa800b5b64e97a7e5a7e5c77ff13927455692cc69070ccdcd12dff2c5e1
|
||||||
|
size 341134
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:920ceaebc2f90401cee2d5b0c26c4fc2f9beada7d9a17dd90843a472111c2eed
|
||||||
|
size 150356
|
||||||
3
uploads/1762603614967-New-chhindwara-image.png
Normal file
3
uploads/1762603614967-New-chhindwara-image.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e181286a94a43674cc5f91f53dc7993a0b35deabc1676b76f6a95d2a82f09dd6
|
||||||
|
size 1296332
|
||||||
3
uploads/1762603765339-new1-chhindwara-image.png
Normal file
3
uploads/1762603765339-new1-chhindwara-image.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e4647cc03eb202cd888fa41ba400e9ff4bfcc8b563280cfbf3035f2fb0fb3893
|
||||||
|
size 1308696
|
||||||
3
uploads/1762750708567-final-chhindwara-image.png
Normal file
3
uploads/1762750708567-final-chhindwara-image.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:8652fc37c2ca1cd4d186e61d5dcaf0528dea22cbc0026704a195f8f091678d4d
|
||||||
|
size 1399820
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:ff2674a0961bce76365d05311048c2866bbfb8e8d0059ede2f5484302eae3be9
|
||||||
|
size 1643635
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:ff2674a0961bce76365d05311048c2866bbfb8e8d0059ede2f5484302eae3be9
|
||||||
|
size 1643635
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:be3d05534aa1434da4744a377e3d2cfd5360d075e7d23a3405812dd62794959d
|
||||||
|
size 258388
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:722ac9b0e5c4ac59a7e098ac380b99bd28b8983a994ce66ab836f3a4ddb5b3a4
|
||||||
|
size 220027
|
||||||
3
uploads/1765176502539-Screenshot (363).png
Normal file
3
uploads/1765176502539-Screenshot (363).png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9d2d6b89d56c55b32fb1fb11f3dc4300e8a53882be08579782ce5866bb2a80a8
|
||||||
|
size 163622
|
||||||
3
uploads/1765178845236-47894 -UTR.jpeg
Normal file
3
uploads/1765178845236-47894 -UTR.jpeg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:49dda7fbecd085642f0f78e9c5d28730cf0a9cdb44098e9a651cbefcc68c503e
|
||||||
|
size 312187
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:3d0704c9283aea311467fed713cf5bec5b1a62c501842a5054ecee148588d25b
|
||||||
|
size 143013
|
||||||
3
uploads/1766058099192-Photo 40.jpeg
Normal file
3
uploads/1766058099192-Photo 40.jpeg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:a4a3fff41cc800142586b213149acbc460d9020c3a26c8c5a52ba27b310e07ad
|
||||||
|
size 198794
|
||||||
3
uploads/1766058106951-Photo 39.jpeg
Normal file
3
uploads/1766058106951-Photo 39.jpeg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6a8da8fa8dd2242160f7a82f0e84fa50f940bb9bb98757d0af005d9fae7e2527
|
||||||
|
size 224299
|
||||||
3
uploads/1766058118709-Photo 28.jpeg
Normal file
3
uploads/1766058118709-Photo 28.jpeg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:30430e08b449c6ef0a3191f76d763316957f931e60630e3e70112d1a8a20cabf
|
||||||
|
size 171577
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fa95b026a5572e1981a34fa471c0db1ef5f7d5cd2a70ae3126eefe04877ba577
|
||||||
|
size 270148
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:71d73cc2e658f634c63e967e84f9040be5ba6fc1e8711d0aaad4689689b7d06d
|
||||||
|
size 244264
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d9a998d16ba7715aebef07321902b0dbf7d79e5e218c1ad4e98e37f7a82a98bc
|
||||||
|
size 195839
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:545b067a2f79fe311739ee81b5e1996e7b39ee0a487c69195e73036fe7120e25
|
||||||
|
size 388066
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:175b8d6158b2d800e0de03337810367e7d6f84eee5bffc1fab29449a9f2be425
|
||||||
|
size 196030
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:2bc83397b5da254b5cde16bfb86012f150600d4cb1fc440519631fd80de47aaa
|
||||||
|
size 194412
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:38c83d7a6909fc5404684fdb3aeb53858447bc4a30194d1176c7c5cb73e3f1e7
|
||||||
|
size 155144
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e9196f24a766e07e8b3fb9e46a3a6e4b6ac583c2eb4521c50e9888bec2b5acd7
|
||||||
|
size 695000
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:267eca9db020fabcb0479b64ab51cd76c26a0afeac137b8c0dae08b0cb77354f
|
||||||
|
size 266099
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:670b1ab2113c16a332d369ace1c1bf5bd593d8410ebb3f420dee5687e4d459df
|
||||||
|
size 247663
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5ae818d036da6886c461772a64b4d1a0667937d178df2b3f8c3e2c54038a622a
|
||||||
|
size 376889
|
||||||
3
uploads/1766137224615-electro.png
Normal file
3
uploads/1766137224615-electro.png
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:061fb64bacdab2875d69fb93d173ea9e37660ddd8bb5a61470cb80c7ba82056f
|
||||||
|
size 38986
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:353a56addc86cf52057f96d712a1e9c284603e48cb6659b270d1795d9d844a65
|
||||||
|
size 217645
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5b2c3909ccaf45150a8fdc370f6d345c1198151f6523d1016e2c8486e4ebdd6c
|
||||||
|
size 239400
|
||||||
15
utils/fileHandler.js
Normal file
15
utils/fileHandler.js
Normal 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 };
|
||||||
Reference in New Issue
Block a user