const userController = require("./controllers/userController"); const { User,Counter, generateBookingId,resetCounter,generateCustomerId,ProfilePicture} = require('./models/User') //const tanksController = require("./controllers/tanksController"); const tankersController = require("./controllers/tankersController.js"); const createConnectionController = require("./controllers/createConnectionController"); const storeController = require("./controllers/storeController.js") const boom = require("boom"); const bcrypt = require('bcrypt'); const { ProfilePictureStore,generateinstallationId,Store, Survey, PlumbingWorkPictures, ElectrictyWorkPictures, MaterialRecievedPictures, Support} = require("./models/store"); const cors = require('fastify-cors'); //const cors = require("cors"); const swagger = require("./config/swagger"); const rawBody = require('raw-body') const uuidv4 = require("uuid").v4; const fastify = require("fastify")({ logger: true, //disableRequestLogging: true, genReqId(req) { // you get access to the req here if you need it - must be a synchronous function return uuidv4(); }, }); // const Fastify = require("fastify"); fastify.register(cors, { origin: 'http://armintaaqua.com:3000', // Allow only your frontend URL methods: ['GET', 'POST', 'PUT', 'DELETE'], // Allowed HTTP methods }); // const Fastify = require("fastify"); // const server = Fastify({ // logger: true, // // ajv: { plugins: [ajvPlugin] }, // genReqId(req) { // // you get access to the req here if you need it - must be a synchronous function // return uuidv4(); // }, // }); // fastify.register(View).ready((err) => { // if (err) console.error(err); // console.log(fastify.config.PORT); // or fastify[options.confKey] // // output: { PORT: 3000 } // engine: { // ejs: require('ejs'), // }, // root: join(__dirname, 'views/html'), // }); const now = () => Date.now(); const fastifyEnv = require("fastify-env"); const schema = { type: "object", required: ["PORT"], properties: { PORT: { type: "string", default: 3000, }, APIVERSION: { type: "string", default: "1.0.0", }, }, }; const options = { confKey: "config", // optional, default: 'config' schema: schema, // data: data // optional, default: process.env }; fastify.register(fastifyEnv, options).ready((err) => { if (err) console.error(err); console.log(fastify.config.PORT); // or fastify[options.confKey] // output: { PORT: 3000 } fastify.decorate("conf", { port: fastify.config.PORT, APIVERSION: fastify.config.APIVERSION, }); }); const apiversion = "1.0.0"; const path = require("path"); // Using static content for swagger documentation. Generated swagger UI is not user friendly. fastify.register(require("fastify-static"), { root: path.join(__dirname, "api-docs"), prefix: "/api-docs", // optional: default '/' }); fastify.register(require("fastify-swagger"), swagger.options); const customJwtAuth = require("./customAuthJwt"); fastify.register(customJwtAuth); //login route - accept user credentials and send a token with role . "user" role is required to use the app. // support login using application/x-www-form-urlencoded so users can login via a web form in addition to api // fastify.register(require("fastify-formbody")); // fastify.register(require('fastify-multipart')) // fastify.register(require("fastify-cors"), { // // put your options here // origin: [ // new RegExp("http://localhost"), // new RegExp("http://simply-backoffice.true2air.com"), // new RegExp("http://localhost:3000"), // ], // credentials: true, // optionsSuccessStatus: 200, // }); fastify.register((fastify, opts, done) => { fastify.addContentTypeParser( "application/json", { parseAs: "buffer" }, function (_req, body, done) { try { done(null, body) } catch (error) { error.statusCode = 400 done(error, undefined) } } ) done(null) }) fastify.register(require('point-of-view'), { engine: { nunjucks: require('nunjucks') }, root: path.join(__dirname, "views"), includeViewExtension: true, }); // * This is for login user as a simply user * fastify.post("/api/login", { schema: { description: "This is for Login User", tags: ["Login"], summary: "This is for User Login", body: { type: "object", required: ["phone", "password"], properties: { phone: { type: "string" }, password: { type: "string" }, fcmIds: { type: "array", items: { type: "string" }, default: [] }, deviceId: { type: "string" }, }, }, }, async handler(req, reply) { const { phone, password, fcmIds, deviceId } = req.body; console.log(password, phone); const loginObject = await userController.loginUser(req, fcmIds, deviceId); if (!loginObject.same) { return reply.send({ simplydata: { error: true, code: 400, message: "Invalid UserId or Password supplied", }, }); } const user = loginObject.user; const phoneVerified = user.phoneVerified; const oneTimePasswordSetFlag = user.oneTimePasswordSetFlag; if (fcmIds.length > 0) { await User.updateOne( { customerId: user.customerId }, { $addToSet: { fcmIds: { $each: fcmIds } } } ); } if (!phoneVerified) { return reply.send({ simplydata: { error: false, phoneVerified: false, phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, oneTimePasswordSetFlag, message: "Please Verify your phone number", }, }); } if (oneTimePasswordSetFlag) { return reply.send({ simplydata: { error: false, phoneVerified, phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, oneTimePasswordSetFlag: true, message: "Password must be reset", }, }); } const tokenPayload = { username: loginObject.isStaff ? loginObject.staffMember.name : user.username, userId: user._id, roles: user.profile.role, }; const token = fastify.jwt.sign(tokenPayload, { expiresIn: "30d" }); const profilePicture = await ProfilePicture.findOne({ customerId: user.customerId }); const responsePayload = { simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, buildingName: user.buildingName, email: user.emails, phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, customerId: user.customerId, username: loginObject.isStaff ? loginObject.staffMember.name : user.username, address1: user.profile.address1, address2: user.profile.address2, phoneVerified: user.phoneVerified, oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, latitude: user.latitude, longitude: user.longitude, type: user.profile.role, loginType: loginObject.isStaff ? "staff" : "user", }, }; if (loginObject.isStaff) { let allMotorAccess = loginObject.staffMember.all_motor_access; // Normalize the value if it matches the given variations if (["view", "view only", "View", "View Only"].includes(allMotorAccess)) { allMotorAccess = "view"; } responsePayload.simplydata.all_motor_access = allMotorAccess; } if (profilePicture) { responsePayload.simplydata.picture = profilePicture.picture; } reply.send(responsePayload); }, }); fastify.post("/api/installotplogin", { schema: { description: "This is for Login Otp Installation", tags: ["Install"], summary: "This is for Login Otp Installation", body: { type: "object", required: ["phone", "phoneVerificationCode"], properties: { phoneVerificationCode: { type: "string" }, phone: { type: "string" }, }, }, }, async handler(req, reply) { try { const phone = req.body.phone; const phoneVerificationCode = req.body.phoneVerificationCode; const installationExists = await Install.findOne({ phone: phone, phoneVerificationCode: phoneVerificationCode, }); if (installationExists) { const filter = { phone: phone, phoneVerificationCode: phoneVerificationCode, }; const update = { phoneVerified: true }; await Install.findOneAndUpdate(filter, update); const loginObject = await userController.loginUserWithOTP(req); if (loginObject.same) { if (loginObject.user) { const { user } = loginObject; const phoneVerified = user.phoneVerified; const oneTimePasswordSetFlag = user.oneTimePasswordSetFlag; if (!phoneVerified) { reply.send({ simplydata: { error: false, phoneVerified: false, phone: user.phone, oneTimePasswordSetFlag: oneTimePasswordSetFlag, message: "Please Verify your phone number", }, }); } else if (oneTimePasswordSetFlag) { reply.send({ simplydata: { error: false, phoneVerified: phoneVerified, phone: user.phone, oneTimePasswordSetFlag: true, message: "Password must be reset", }, }); } else { const token = fastify.jwt.sign( { firstName: user.firstName, }, { expiresIn: "30d" } ); const profilePicture = await ProfilePictureInstall.findOne({ customerId: user._id, }); console.log(user) const responsePayload = { simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, email: user.emails, installationId: user.installationId, phone: user.phone, //name: user.name, address1: user.address1, address2: user.address2, phoneVerified: user.phoneVerified, oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, type: user.profile.role, fcmIds: user.fcmIds, team: user.team, city: user.city, manager: user.manager, firstName: user.firstName, lastName: user.lastName, address: user.address, alternativeNumber: user.alternativeNumber, }, }; if (profilePicture) { responsePayload.simplydata.picture = profilePicture.picture; } reply.send(responsePayload); } } else { reply.send({ simplydata: { error: true, code: 400, message: "Invalid Details", }, }); } } else { reply.send({ simplydata: { error: true, code: 400, message: "Invalid phone or phoneVerificationCode supplied", }, }); } } else { reply.send({ armintatankdata: { error: true, code: 10005, message: "10005 - Verification code entered cannot be validated.", }, }); } } catch (err) { throw boom.boomify(err); } }, }); // fastify.post("/api/storelogin", { // schema: { // description: "This is for Store Login", // tags: ["Store-Data"], // summary: "This is for Store Login", // body: { // type: "object", // required: ["phone", "password"], // properties: { // phone: { type: "string" }, // password: { type: "string" }, // }, // }, // }, // async handler(request, reply) { // try { // let store = await Store.findOne({ phone: request.body.phone }); // if (!store) { // return reply.code(400).send({ // simplydata: { // error: true, // code: 400, // message: "Invalid Phone or Password", // }, // }); // } // const isMatch = await bcrypt.compare(request.body.password, store.services.password.bcrypt); // if (!isMatch) { // return reply.code(400).send({ // simplydata: { // error: true, // code: 400, // message: "Invalid Phone or Password", // }, // }); // } // const token = fastify.jwt.sign( // { // storename: store.storename, // storeId: store._id, // roles: store.profile.role, // }, // { expiresIn: "30d" } // ); // var profilePicture = await ProfilePictureStore.findOne({ storeId: store.storeId }); // if (!profilePicture) { // reply.send({ // simplydata: { // error: false, // apiversion: fastify.config.APIVERSION, // access_token: token, // email: store.emails, // phone: store.phone, // storeId: store.storeId, // storename: store.storename, // office_address: store.profile.office_address, // phoneVerified: store.phoneVerified, // oneTimePasswordSetFlag: store.oneTimePasswordSetFlag, // latitude: store.latitude, // longitude: store.longitude, // description: store.description, // type: store.profile.role, // typeasobj: JSON.stringify(Object.assign({}, store.profile.role)), // }, // }); // } else { // reply.send({ // simplydata: { // error: false, // apiversion: fastify.config.APIVERSION, // access_token: token, // picture: profilePicture.picture, // email: store.emails, // phone: store.phone, // storeId: store.storeId, // storename: store.storename, // office_address: store.profile.office_address, // phoneVerified: store.phoneVerified, // oneTimePasswordSetFlag: store.oneTimePasswordSetFlag, // latitude: store.latitude, // longitude: store.longitude, // description: store.description, // type: store.profile.role, // typeasobj: JSON.stringify(Object.assign({}, store.profile.role)), // }, // }); // } // } catch (err) { // throw boom.boomify(err); // } // }, // }); fastify.get("/api/reset_token/:customerId", { schema: { description: "This is for Reset Token", tags: ["Login"], summary: "This is for Reset Token", params: { type: "object", properties: { customerId: { type: "string", description: "customerId", }, }, }, }, async handler(req, reply) { try { const customerId = req.params.customerId const get_user = await userController.getSingleUser(req); const token = fastify.jwt.sign( { customerId: get_user.customerId, userId: get_user._id, roles: get_user.profile.role, }, { expiresIn: "30d" } ); reply.send({ access_token: token, customerId: get_user.customerId }); } catch (err) { console.log(err); error = { simplydata: { error: true, code: 400, message: "Reset Token failed", }, }; reply.status(401).send(error); } }, }); fastify.get('/testtemp', (req, reply) => { reply.view('layouts/main', {}); }); // const multipart = require('fastify-multipart'); // fastify.register(multipart); //fastify-auth plugin is required so we can define routes in seperate files and verify jwt supplied in preHandlers for each request. //const multer = require("fastify-multer"); fastify.register(require("fastify-auth")); const dbConnection = require("./config/config"); fastify.register(dbConnection); //fastify.register(multer.contentParser); const { Schema } = require("mongoose"); // fastify.register(dbConnection); fastify.register(require("./routes/usersRoute")); fastify.register(require("./routes/tanksRoute")); fastify.register(require("./routes/createConnectionsRoute")); fastify.register(require("./routes/tankersRoute.js")); fastify.register(require("./routes/supplierRoute")); fastify.register(require("./routes/supplierOrdersRoutes")); fastify.register(require("./routes/friendRequestRoute")); fastify.register(require("./routes/adminRoute")); fastify.register(require("./routes/storeRoute")); fastify.register(require("./routes/departmentRoute.js")); fastify.register(require("./routes/installationRoute.js")); // Testing route allows for retrieving a user by phone so one can see what is the phone verification code sent for a given user's phone // Also allows deletion of a user with a given phone number fastify.register(require("./routes/forTestingRoute")); const fs = require('fs'); const {Storage} = require('@google-cloud/storage'); const { Supplier, profilePictureSupplier } = require("./models/supplier"); const multer = require('fastify-multer'); const { ProfilePictureInstall, Install } = require("./models/store.js"); const { TeamMemberProfilePicture, CompanyProfilePicture, Deparments, IndianLocations } = require("./models/Department.js"); fastify.register(require('fastify-formbody')); // fastify.register(multer.contentParser); // const multipart = require('fastify-multipart'); // fastify.register(multipart); const gc = new Storage({ keyFilename : path.join(__dirname, "../src/arminta-tank-keyFile.json"), projectId : 'arminta-tank' }) const storage = new Storage({ keyFilename : path.join(__dirname, "../src/arminta-tank-keyFile.json"), projectId : 'arminta-tank' }); // console.log(storage) // const cloudinary = require('cloudinary').v2; // const FormData = require('form-data'); // const mv = require('mv'); // cloudinary.config({ // cloud_name: 'dalqpseol', // api_key: '121595628244491', // api_secret: 'jnuny_0fMYovQS0eyvIVXQTl4RY' // }); // Register fastify-file-upload plugin fastify.register(require('fastify-multipart')); // fastify.post('/upload', async (request, reply) => { // try { // const data = await request.file(); // // Generate a unique file name // const fileName = `${data.filename}`; // const filePath = `${fileName}`; // // Move the file to a temporary location // const writeStream = fs.createWriteStream(filePath); // data.file.pipe(writeStream); // writeStream.on('close', () => { // // Upload the image to Cloudinary // cloudinary.uploader.upload(filePath, (error, result) => { // if (error) { // reply.code(500).send({ error: 'Failed to upload file to Cloudinary' }); // } else { // // Get the public URL of the uploaded image // const publicUrl = result.secure_url; // // Remove the temporary file // fs.unlinkSync(filePath); // // Return the public URL // reply.send({ url: publicUrl }); // } // }); // }); // writeStream.on('error', (err) => { // reply.code(500).send({ error: 'Failed to move file' }); // }); // } catch (err) { // reply.code(500).send({ error: 'An error occurred' }); // } // }); // fastify.post('/upload/:supplierId', async (request, reply) => { // try { // const supplierId = request.params.supplierId; // const data = await request.file(); // // Generate a unique file name // const fileName = `${data.filename}`; // const filePath = `${fileName}`; // // Move the file to a temporary location // const writeStream = fs.createWriteStream(filePath); // data.file.pipe(writeStream); // writeStream.on('close', () => { // // Upload the image to Cloudinary // cloudinary.uploader.upload(filePath, (error, result) => { // if (error) { // reply.code(500).send({ error: 'Failed to upload file to Cloudinary' }); // } else { // // Get the public URL of the uploaded image // const publicUrl = result.secure_url; // // Remove the temporary file // fs.unlinkSync(filePath); // // Store the URL in the database // // Assuming you have a database connection and a ProfilePictureSupplier model // profilePictureSupplier.findOneAndUpdate( // { supplierId }, // { picture: publicUrl }, // { new: true, upsert: true }, // (error, picture) => { // if (error) { // reply.code(500).send({ error: 'Failed to update database' }); // } else { // // Return the public URL // reply.send({ picture: publicUrl }); // } // } // ); // } // }); // }); // writeStream.on('error', (err) => { // reply.code(500).send({ error: 'Failed to move file' }); // }); // } catch (err) { // reply.code(500).send({ error: 'An error occurred' }); // } // }); fastify.post('/api/uploads_team_profile/:customerId', async (request, reply) => { try { const customerId = request.params.customerId; const data = await request.file(); const bucketName = 'arminta_profile_pictures'; const filePath = `arminta_team_profiles/${data.filename}`; const file = storage.bucket(bucketName).file(filePath); const writeStream = file.createWriteStream(); data.file.pipe(writeStream); await new Promise((resolve, reject) => { writeStream.on('finish', resolve); writeStream.on('error', reject); }); // Make file public await file.makePublic(); const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`; // Update DB with async/await const picture = await TeamMemberProfilePicture.findOneAndUpdate( { customerId }, { picture: publicUrl }, { new: true, upsert: true } ); reply.send({ picture: publicUrl }); } catch (err) { console.error(err); reply.code(500).send({ error: 'An error occurred', details: err.message }); } }); fastify.post('/api/uploads_company_profile/:customerId', async (request, reply) => { try { const customerId = request.params.customerId; const data = await request.file(); const bucketName = 'arminta_profile_pictures'; const filePath = `arminta_company_profiles/${data.filename}`; const file = storage.bucket(bucketName).file(filePath); const writeStream = file.createWriteStream(); data.file.pipe(writeStream); await new Promise((resolve, reject) => { writeStream.on('finish', resolve); writeStream.on('error', reject); }); // Make file public await file.makePublic(); const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`; // Update DB with async/await const picture = await CompanyProfilePicture.findOneAndUpdate( { customerId }, { picture: publicUrl }, { new: true, upsert: true } ); reply.send({ picture: publicUrl }); } catch (err) { console.error(err); reply.code(500).send({ error: 'An error occurred', details: err.message }); } }); fastify.post('/api/uploads/:supplierId', async (request, reply) => { try { const supplierId = request.params.supplierId; const data = await request.file(); // Generate a unique file name const fileName = `${data.filename}`; // Define the destination bucket and file path const bucketName = 'arminta_profile_pictures'; const filePath = `${fileName}`; // Create a write stream to the destination file in the bucket const writeStream = storage.bucket(bucketName).file(filePath).createWriteStream(); // Pipe the file data to the write stream data.file.pipe(writeStream); writeStream.on('finish', async () => { try { // Make the uploaded file publicly accessible await storage.bucket(bucketName).file(filePath).makePublic(); const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`; profilePictureSupplier.findOneAndUpdate( { supplierId }, { picture: publicUrl }, { new: true, upsert: true }, (error, picture) => { if (error) { reply.code(500).send({ error: 'Failed to update database' }); } else { // Return the public URL reply.send({ picture: publicUrl }); } } ); } catch (error) { reply.code(500).send({ error: 'Failed to make file public' }); } }); writeStream.on('error', (err) => { reply.code(500).send({ error: 'Failed to move file' }); }); } catch (err) { reply.code(500).send({ error: 'An error occurred' }); } }); fastify.post('/api/uploads-user/:customerId', async (request, reply) => { try { const customerId = request.params.customerId; const data = await request.file(); // Generate a unique file name const fileName = `${data.filename}`; // Define the destination bucket and file path const bucketName = 'arminta_profile_pictures'; const filePath = `arminta_user_profiles/${fileName}`; // Create a write stream to the destination file in the bucket const writeStream = storage.bucket(bucketName).file(filePath).createWriteStream(); // Pipe the file data to the write stream data.file.pipe(writeStream); writeStream.on('finish', async () => { try { // Make the uploaded file publicly accessible await storage.bucket(bucketName).file(filePath).makePublic(); const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`; ProfilePicture.findOneAndUpdate( { customerId }, { picture: publicUrl }, { new: true, upsert: true }, (error, picture) => { if (error) { reply.code(500).send({ error: 'Failed to update database' }); } else { // Return the public URL reply.send({ picture: publicUrl }); } } ); } catch (error) { reply.code(500).send({ error: 'Failed to make file public' }); } }); writeStream.on('error', (err) => { reply.code(500).send({ error: 'Failed to move file' }); }); } catch (err) { reply.code(500).send({ error: 'An error occurred' }); } }); fastify.post("/api/uploads-electricty-work/:customerId/:installationId", async (request, reply) => { try { const { customerId, installationId } = request.params; const files = await request.files(); // Await files properly if (!files || files.length === 0) { return reply.code(400).send({ error: "No files uploaded" }); } const bucketName = "arminta_profile_pictures"; const publicUrls = []; for await (const file of files) { const uniqueFileName = `${Date.now()}-${Math.random().toString(36).substring(7)}-${file.filename}`; const filePath = `electricty_work_picture/${uniqueFileName}`; console.log(`Uploading file: ${file.filename} → ${filePath}`); const writeStream = storage.bucket(bucketName).file(filePath).createWriteStream(); file.file.pipe(writeStream); await new Promise((resolve, reject) => { writeStream.on("finish", async () => { try { await storage.bucket(bucketName).file(filePath).makePublic(); const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`; publicUrls.push(publicUrl); console.log(`File uploaded: ${publicUrl}`); resolve(); } catch (error) { console.error("Failed to make file public:", error); reject(error); } }); writeStream.on("error", (err) => { console.error("Failed to upload file:", err); reject(err); }); }); } // Update MongoDB: Convert URLs to { url: "..." } objects const updatedRecord = await ElectrictyWorkPictures.findOneAndUpdate( { customerId, installationId }, { $push: { pictureUrl: { $each: publicUrls.map(url => ({ url })) } } }, // Append new images { new: true, upsert: true } ); reply.send({ success: true, pictures: publicUrls, details: updatedRecord }); } catch (err) { console.error("Upload Error:", err); reply.code(500).send({ error: "An error occurred", details: err.message }); } }); fastify.post("/api/uploads-plumbing-work/:customerId/:installationId", async (request, reply) => { try { const { customerId, installationId } = request.params; const files = await request.files(); // Await files properly if (!files || files.length === 0) { return reply.code(400).send({ error: "No files uploaded" }); } const bucketName = "arminta_profile_pictures"; const publicUrls = []; for await (const file of files) { const uniqueFileName = `${Date.now()}-${Math.random().toString(36).substring(7)}-${file.filename}`; const filePath = `plumbing_work_picture/${uniqueFileName}`; console.log(`Uploading file: ${file.filename} → ${filePath}`); const writeStream = storage.bucket(bucketName).file(filePath).createWriteStream(); file.file.pipe(writeStream); await new Promise((resolve, reject) => { writeStream.on("finish", async () => { try { await storage.bucket(bucketName).file(filePath).makePublic(); const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`; publicUrls.push(publicUrl); console.log(`File uploaded: ${publicUrl}`); resolve(); } catch (error) { console.error("Failed to make file public:", error); reject(error); } }); writeStream.on("error", (err) => { console.error("Failed to upload file:", err); reject(err); }); }); } // Update MongoDB: Convert URLs to { url: "..." } objects const updatedRecord = await PlumbingWorkPictures.findOneAndUpdate( { customerId, installationId }, { $push: { pictureUrl: { $each: publicUrls.map(url => ({ url })) } } }, // Append new images { new: true, upsert: true } ); reply.send({ success: true, pictures: publicUrls, details: updatedRecord }); } catch (err) { console.error("Upload Error:", err); reply.code(500).send({ error: "An error occurred", details: err.message }); } }); fastify.post("/api/uploads-material-recieved/:customerId/:installationId", async (request, reply) => { try { const { customerId, installationId } = request.params; const files = await request.files(); // Await files properly if (!files || files.length === 0) { return reply.code(400).send({ error: "No files uploaded" }); } const bucketName = "arminta_profile_pictures"; const publicUrls = []; for await (const file of files) { const uniqueFileName = `${Date.now()}-${Math.random().toString(36).substring(7)}-${file.filename}`; const filePath = `plumbing_work_picture/${uniqueFileName}`; console.log(`Uploading file: ${file.filename} → ${filePath}`); const writeStream = storage.bucket(bucketName).file(filePath).createWriteStream(); file.file.pipe(writeStream); await new Promise((resolve, reject) => { writeStream.on("finish", async () => { try { await storage.bucket(bucketName).file(filePath).makePublic(); const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`; publicUrls.push(publicUrl); console.log(`File uploaded: ${publicUrl}`); resolve(); } catch (error) { console.error("Failed to make file public:", error); reject(error); } }); writeStream.on("error", (err) => { console.error("Failed to upload file:", err); reject(err); }); }); } // Update MongoDB: Convert URLs to { url: "..." } objects const updatedRecord = await MaterialRecievedPictures.findOneAndUpdate( { customerId, installationId }, { $push: { pictureUrl: { $each: publicUrls.map(url => ({ url })) } } }, // Append new images { new: true, upsert: true } ); reply.send({ success: true, pictures: publicUrls, details: updatedRecord }); } catch (err) { console.error("Upload Error:", err); reply.code(500).send({ error: "An error occurred", details: err.message }); } }); fastify.post("/api/installLogin", { schema: { description: "This is for Login Install", tags: ["Installation"], summary: "This is for Login Install", body: { type: "object", required: ["type", "phone", "password"], properties: { type: { type: "string", description: "User role type (e.g., 'admin', 'manager')" }, phone: { type: "string", description: "Registered phone number" }, password: { type: "string", description: "Password for authentication" }, }, }, }, async handler(req, reply) { try { const { type, phone, password } = req.body; // Check if user exists in the Department Schema const user = await Deparments.findOne({ phone }); if (!user) { return reply.code(400).send({ message: "User not found" }); } // Verify Password const isMatch = await bcrypt.compare(password, user.services.password.bcrypt); if (!isMatch) { return reply.code(400).send({ message: "Invalid credentials" }); } // Check if department details already exist in installation schema let installation = await Install.findOne({ phone }); if (!installation) { // Create a new entry in installation schema with departmentId as installationId installation = new Install({ phone: user.phone, installationId: user.departmentId, // Store departmentId in installationId firstName: user.firstName, lastName: user.lastName, email: user.email, alternativeNumber: user.alternativeContactNumber, departmentName: user.departmentName, designation: user.desginationName, reportingManager: user.reportingManager, city: user.city, zone: user.zone, address1: user.address1, address2: user.address2, profile: { state: user.state, country: user.country, role: type, // Store type in profile.role }, }); await installation.save(); } // Ensure `type` is stored in `profile.role` if (!installation.profile?.role) { installation.profile.role = type; await installation.save(); // Save the updated type } // Fetch profile picture if available const profilePicture = await ProfilePictureInstall.findOne({ customerId: installation._id }); // Generate JWT Token const token = fastify.jwt.sign( { userId: user._id, phone: user.phone, role: installation.profile?.role }, "your_secret_key", { expiresIn: "7d" } ); // Construct response payload const responsePayload = { simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, email: installation.emails || [], installationId: installation.installationId, phone: installation.phone, address1: installation.address1, address2: installation.address2, phoneVerified: installation.phoneVerified, oneTimePasswordSetFlag: installation.oneTimePasswordSetFlag, type: installation.profile?.role || "user", // Default to "user" if not available fcmIds: installation.fcmId || null, team: installation.team, city: installation.city, manager: installation.manager, firstName: installation.firstName, lastName: installation.lastName, address: installation.address || "", alternativeNumber: installation.alternativeNumber || null, profilePicture: profilePicture ? profilePicture.pictureUrl : null, // Include profile picture URL if available }, }; return reply.send(responsePayload); } catch (error) { console.error("Login Error:", error); return reply.code(500).send({ message: "Internal server error" }); } }, }); fastify.post("/api/teamMemberLogin", { schema: { description: "Login API for team members", tags: ["Installation"], summary: "Login as a Team Member", body: { type: "object", required: ["type", "phone", "password"], properties: { type: { type: "string", description: "Role type of the user (e.g., 'team_member')" }, phone: { type: "string", description: "Registered phone number of the team member" }, password: { type: "string", description: "Password for authentication" }, }, }, }, async handler(request, reply) { try { const { type, phone, password } = request.body; // ✅ Step 1: Find the team member in `Deparments` const department = await Deparments.findOne({ "team_member.team_member.phone": phone }); if (!department) { return reply.status(401).send({ simplydata: { error: true, message: "Invalid phone number", }, }); } // ✅ Step 2: Find the specific team member const teamMember = department.team_member.team_member.find( (member) => member.phone === phone ); if (!teamMember) { return reply.status(401).send({ simplydata: { error: true, message: "Invalid phone number", }, }); } // ✅ Step 3: Verify password const isPasswordValid = await bcrypt.compare(password, teamMember.password); if (!isPasswordValid) { return reply.status(401).send({ simplydata: { error: true, message: "Invalid phone number or password", }, }); } console.log("Team Member First Name:", teamMember.firstName); // ✅ Debugging // ✅ Step 4: Check if this team member already exists in `Install` let installation = await Install.findOne({ installationId: department.departmentId }); if (!installation) { return reply.status(404).send({ simplydata: { error: true, message: "Installation not found", }, }); } // Check if team member already exists in Install schema const existingTeamMember = installation.team_member.team_member.find( (member) => member.phone === phone ); if (!existingTeamMember) { // ✅ Step 5: Add team member details to `Install` schema installation.team_member.team_member.push({ teamMemberId: teamMember.teamMemberId, firstName: teamMember.firstName, phone: teamMember.phone, email: teamMember.email, alternativePhone: teamMember.alternativePhone, installationTeamMemId: installation.installationId, password: teamMember.password, // Store hashed password status: teamMember.status || "active", type: type, // Store login type }); await installation.save(); } // ✅ Step 6: Generate JWT token const token = fastify.jwt.sign( { phone: teamMember.phone, role: type, installationId: installation.installationId }, "JWT_SECRET", { expiresIn: "1h" } ); return reply.send({ simplydata: { error: false, message: "Login successful", access_token: token, phone: teamMember.phone, firstName: teamMember.firstName || null, // ✅ Now included teamMemberId: teamMember.teamMemberId, alternativePhone: teamMember.alternativePhone || null, email: teamMember.email || null, status: teamMember.status || "active", type: teamMember.type, installationId: installation.installationId }, }); } catch (err) { console.error("Error logging in:", err); reply.status(500).send({ simplydata: { error: true, message: "Internal server error", }, }); } }, }); fastify.post("/api/surveyLogin", { schema: { description: "This is for Login Survey", tags: ["Survey"], summary: "This is for Login Survey", body: { type: "object", required: [ "phone", "password"], properties: { phone: { type: "string", description: "Registered phone number" }, password: { type: "string", description: "Password for authentication" }, }, }, }, async handler(req, reply) { try { const { phone, password } = req.body; // Check if user exists in the Department Schema const user = await Deparments.findOne({ phone }); console.log("user", user) if (!user) { return reply.code(400).send({ message: "User not found" }); } // Verify Password const isMatch = await bcrypt.compare(password, user.services.password.bcrypt); if (!isMatch) { return reply.code(400).send({ message: "Invalid credentials" }); } let survey = await Survey.findOne({ phone }); if (!survey) { survey = new Survey({ phone: user.phone, surveyId: user.departmentId, firstName: user.firstName, lastName: user.lastName, email: user.email, alternativeNumber: user.alternativeContactNumber, departmentName: user.departmentName, designation: user.desginationName, reportingManager: user.reportingManager, city: user.city, zone: user.zone, address1: user.address1, address2: user.address2, profile: { state: user.state, country: user.country, //role: type, // Store type in profile.role }, }); await survey.save(); } const token = fastify.jwt.sign( { phone: user.phone }, "Scret", { expiresIn: "1h" } ); return reply.send({ simplydata: { error: false, message: "Login successful", access_token: token, phone: user.phone, surveyId: user.departmentId, firstName: user.firstName, lastName: user.lastName, email: user.email, alternativeNumber: user.alternativeContactNumber, departmentName: user.departmentName, designation: user.desginationName, reportingManager: user.reportingManager, city: user.city, zone: user.zone, address1: user.address1, address2: user.address2, profile: { state: user.state, country: user.country, //role: type, // Store type in profile.role }, }, }); // return reply.send(survey); } catch (error) { console.error("Login Error:", error); return reply.code(500).send({ message: "Internal server error" }); } }, }); fastify.post("/api/supportLogin", { schema: { description: "This is for Login Support", tags: ["Support"], summary: "This is for Login Support", body: { type: "object", required: ["phone", "password", "type"], properties: { phone: { type: "string", description: "Registered phone number" }, password: { type: "string", description: "Password for authentication" }, type: { type: "string", description: "Role of the user (e.g., 'Support_Manager')" }, }, }, }, async handler(req, reply) { try { const { phone, password, type } = req.body; const user = await Deparments.findOne({ phone }); if (!user) { return reply.code(400).send({ message: "User not found" }); } const isMatch = await bcrypt.compare(password, user.services.password.bcrypt); if (!isMatch) { return reply.code(400).send({ message: "Invalid credentials" }); } let survey = await Support.findOne({ phone }); if (!survey) { survey = new Support({ phone: user.phone, supportId: user.departmentId, firstName: user.firstName, lastName: user.lastName, email: user.email, alternativeNumber: user.alternativeContactNumber, departmentName: user.departmentName, designation: user.desginationName, reportingManager: user.reportingManager, city: user.city, zone: user.zone, address1: user.address1, address2: user.address2, profile: { state: user.state, country: user.country, }, }); await survey.save(); } const token = fastify.jwt.sign( { userId: user._id, phone: user.phone, role: type }, "Scret", { expiresIn: "1h" } ); return reply.send({ simplydata: { error: false, apiversion: "1.0.0", access_token: token, email: user.email || [], supportId: user.departmentId || "", phone: user.phone, address1: user.address1 || "", address2: user.address2 || "", phoneVerified: false, oneTimePasswordSetFlag: false, type: type, // <== returned from the request fcmIds: null, team: null, city: user.city || "", manager: user.reportingManager || null, firstName: user.firstName || "", lastName: user.lastName || "", address: "", alternativeNumber: user.alternativeContactNumber || "", profilePicture: null } }); } catch (error) { console.error("Login Error:", error); return reply.code(500).send({ message: "Internal server error" }); } }, }); fastify.post("/api/supportTeamMemberLogin", { schema: { description: "Login Support TeamMember", tags: ["Support"], summary: "Login for Support TeamMember", body: { type: "object", required: ["type", "phone", "password"], properties: { type: { type: "string", description: "Role type of the user (e.g., 'Support_TeamMember')" }, phone: { type: "string", description: "Registered phone number of the team member" }, password: { type: "string", description: "Password for authentication" } } } }, async handler(req, reply) { try { const { type, phone, password } = req.body; if (type !== "Support_TeamMember") { return reply.code(400).send({ error: "Invalid user type" }); } const support = await Support.findOne({ "team_member.team_member.phone": phone }); if (!support) { return reply.code(404).send({ error: 'Team member not found' }); } const teamMember = support.team_member.team_member.find( member => member.phone === phone ); if (!teamMember) { return reply.code(404).send({ error: 'Team member not found' }); } if (!teamMember.password) { return reply.code(401).send({ error: 'Password not set' }); } const isPasswordValid = await bcrypt.compare(password, teamMember.password); if (!isPasswordValid) { return reply.code(401).send({ error: 'Invalid credentials' }); } const token = fastify.jwt.sign( { support_teamMemberId: teamMember.support_teamMemberId, name: teamMember.name, phone: teamMember.phone, type: type }, "JWT_SECRET", { expiresIn: '7d' } ); return reply.send({ simplydata: { error: false, apiversion: "1.0.0", access_token: token, email: [], support_teamMemberId: teamMember.support_teamMemberId || "", phone: teamMember.phone, address1: teamMember.address1 || "", address2: teamMember.address2 || "", phoneVerified: false, oneTimePasswordSetFlag: false, type: type, fcmIds: null, team: null, city: teamMember.city || "", manager: null, firstName: teamMember.name?.split(" ")[0] || "", lastName: teamMember.name?.split(" ")[1] || "", address: "", alternativeNumber: teamMember.alternativeNumber || "", profilePicture: null } }); } catch (error) { console.error("Error during team member login:", error); return reply.code(500).send({ error: "Internal server error" }); } } }); fastify.post("/api/storelogin", { schema: { description: "This is for Store Login", tags: ["Store-Data"], summary: "This is for Store Login", body: { type: "object", required: [ "phone", "password"], properties: { phone: { type: "string", description: "Registered phone number" }, password: { type: "string", description: "Password for authentication" }, }, }, }, async handler(req, reply) { try { const { phone, password } = req.body; // Check if user exists in the Department Schema const user = await Deparments.findOne({ phone }); console.log("user", user) if (!user) { return reply.code(400).send({ message: "User not found" }); } // Verify Password const isMatch = await bcrypt.compare(password, user.services.password.bcrypt); if (!isMatch) { return reply.code(400).send({ message: "Invalid credentials" }); } let store = await Store.findOne({ phone }); if (!store) { store = new Store({ phone: user.phone, storeId: user.departmentId, firstName: user.firstName, lastName: user.lastName, email: user.email, alternativeNumber: user.alternativeContactNumber, departmentName: user.departmentName, designation: user.desginationName, reportingManager: user.reportingManager, city: user.city, zone: user.zone, address1: user.address1, address2: user.address2, profile: { state: user.state, country: user.country, //role: type, // Store type in profile.role }, }); await store.save(); } const token = fastify.jwt.sign( { phone: user.phone }, "Scret", { expiresIn: "1h" } ); return reply.send({ simplydata: { error: false, message: "Login successful", access_token: token, phone: user.phone, storeId: user.departmentId, firstName: user.firstName, lastName: user.lastName, email: user.email, alternativeNumber: user.alternativeContactNumber, departmentName: user.departmentName, designation: user.desginationName, reportingManager: user.reportingManager, city: user.city, zone: user.zone, address1: user.address1, address2: user.address2, profile: { state: user.state, country: user.country, //role: type, // Store type in profile.role }, }, }); // return reply.send(survey); } catch (error) { console.error("Login Error:", error); return reply.code(500).send({ message: "Internal server error" }); } }, }); fastify.post("/add-states", async (request, reply) => { try { const statesData = [ { state: "Andhra Pradesh", majorCities: [ "Adoni", "Amaravati", "Anantapur", "Chandragiri", "Chittoor", "Guntur", "Kadapa", "Kakinada", "Kurnool", "Machilipatnam", "Rajahmundry", "Tirupati", "Vijayawada", "Visakhapatnam", "Vizianagaram" ], }, { state: "Arunachal Pradesh", majorCities: ["Itanagar", "Naharlagun", "Tawang", "Pasighat", "Ziro"], }, { state: "Assam", majorCities: ["Guwahati", "Dibrugarh", "Silchar", "Jorhat", "Tezpur"], }, { state: "Bihar", majorCities: ["Patna", "Gaya", "Bhagalpur", "Muzaffarpur", "Purnia"], }, { state: "Chhattisgarh", majorCities: ["Raipur", "Bhilai", "Bilaspur", "Durg", "Korba"], }, { state: "Goa", majorCities: ["Panaji", "Margao", "Vasco da Gama", "Mapusa"], }, { state: "Gujarat", majorCities: ["Ahmedabad", "Surat", "Vadodara", "Rajkot", "Bhavnagar"], }, { state: "Haryana", majorCities: ["Chandigarh", "Faridabad", "Gurugram", "Hisar", "Panipat"], }, { state: "Himachal Pradesh", majorCities: ["Shimla", "Manali", "Dharamshala", "Mandi", "Solan"], }, { state: "Jharkhand", majorCities: ["Ranchi", "Jamshedpur", "Dhanbad", "Bokaro", "Hazaribagh"], }, { state: "Karnataka", majorCities: ["Bengaluru", "Mysuru", "Hubballi", "Mangaluru", "Belagavi"], }, { state: "Kerala", majorCities: ["Thiruvananthapuram", "Kochi", "Kozhikode", "Thrissur", "Alappuzha"], }, { state: "Madhya Pradesh", majorCities: ["Bhopal", "Indore", "Gwalior", "Jabalpur", "Ujjain"], }, { state: "Maharashtra", majorCities: ["Mumbai", "Pune", "Nagpur", "Nashik", "Aurangabad"], }, { state: "Manipur", majorCities: ["Imphal", "Bishnupur", "Thoubal", "Churachandpur"], }, { state: "Meghalaya", majorCities: ["Shillong", "Tura", "Jowai", "Nongstoin"], }, { state: "Mizoram", majorCities: ["Aizawl", "Lunglei", "Champhai", "Serchhip"], }, { state: "Nagaland", majorCities: ["Kohima", "Dimapur", "Mokokchung", "Tuensang"], }, { state: "Odisha", majorCities: ["Bhubaneswar", "Cuttack", "Rourkela", "Berhampur", "Sambalpur"], }, { state: "Punjab", majorCities: ["Chandigarh", "Ludhiana", "Amritsar", "Jalandhar", "Patiala"], }, { state: "Rajasthan", majorCities: ["Jaipur", "Jodhpur", "Udaipur", "Kota", "Ajmer"], }, { state: "Sikkim", majorCities: ["Gangtok", "Namchi", "Mangan", "Gyalshing"], }, { state: "Tamil Nadu", majorCities: ["Chennai", "Coimbatore", "Madurai", "Tiruchirappalli", "Salem"], }, { state: "Telangana", majorCities: ["Hyderabad", "Warangal", "Nizamabad", "Karimnagar", "Khammam"], }, { state: "Tripura", majorCities: ["Agartala", "Udaipur", "Dharmanagar", "Kailashahar"], }, { state: "Uttar Pradesh", majorCities: ["Lucknow", "Kanpur", "Varanasi", "Agra", "Meerut"], }, { state: "Uttarakhand", majorCities: ["Dehradun", "Haridwar", "Nainital", "Rishikesh"], }, { state: "West Bengal", majorCities: ["Kolkata", "Howrah", "Durgapur", "Siliguri", "Asansol"], }, { state: "Andaman and Nicobar Islands", majorCities: ["Port Blair"], }, { state: "Chandigarh", majorCities: ["Chandigarh"], }, { state: "Dadra and Nagar Haveli and Daman and Diu", majorCities: ["Daman", "Diu", "Silvassa"], }, { state: "Lakshadweep", majorCities: ["Kavaratti"], }, { state: "Delhi", majorCities: ["New Delhi"], }, { state: "Puducherry", majorCities: ["Puducherry", "Karaikal", "Mahe", "Yanam"], }, ]; await IndianLocations.deleteMany(); await IndianLocations.insertMany(statesData); reply.send({ message: "✅ All states added successfully!" }); } catch (error) { reply.status(500).send({ error: "❌ Error inserting states: " + error }); } }); // Run the server! const start = async () => { try { await fastify.listen(3000, "0.0.0.0"); fastify.log.info(`listening on ${fastify.server.address().port}`); fastify.log.info(`server listening on ${fastify.config}`); } catch (err) { fastify.log.error(err); process.exit(1); } }; start();