diff --git a/backdoors/.gitignore b/backdoors/.gitignore new file mode 100644 index 0000000..31cb165 --- /dev/null +++ b/backdoors/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules/ +npm_debug.log* \ No newline at end of file diff --git a/backdoors/batchUpload.js b/backdoors/batchUpload.js new file mode 100644 index 0000000..837aef2 --- /dev/null +++ b/backdoors/batchUpload.js @@ -0,0 +1,109 @@ +const fs = require("fs"); +const path = require("path"); +const { MongoClient } = require("mongodb"); +const axios = require("axios"); +const FormData = require("form-data"); +require("dotenv").config(); + +// MongoDB configuration +const MONGO_URI = process.env.MONGO_URI; +const DB_NAME = "Alpha"; // Your database name +const COLLECTION_NAME = "images"; // Your collection name + +// Cloudinary configuration +const CLOUDINARY_URL = "https://api.cloudinary.com/v1_1/dhjyjsyvt/image/upload"; +const UPLOAD_PRESET = "KisanSahayak"; + +// Function to upload image to Cloudinary +async function uploadToCloudinary(filePath) { + try { + console.log(`Uploading to Cloudinary: ${filePath}`); + + const formData = new FormData(); + formData.append("file", fs.createReadStream(filePath)); + formData.append("upload_preset", UPLOAD_PRESET); + + const headers = formData.getHeaders(); // Manually get headers from formData + + const response = await axios.post(CLOUDINARY_URL, formData, { + headers: { + ...headers, // Spread the headers from formData + }, + }); + + if (response.status === 200 && response.data.secure_url) { + console.log(`Uploaded successfully: ${response.data.secure_url}`); + return response.data.secure_url; + } else { + console.error(`Failed to upload: ${filePath}`); + return null; + } + } catch (error) { + console.error(`Cloudinary upload failed: ${error.message}`); + return null; + } +} + +async function uploadImagesToMongo(baseDir) { + const client = new MongoClient(MONGO_URI); + + try { + console.log("Connecting to MongoDB..."); + await client.connect(); + const db = client.db(DB_NAME); + const collection = db.collection(COLLECTION_NAME); + console.log("Connected to MongoDB successfully!"); + + // Get folder name from baseDir to extract crop and disease + const folderName = path.basename(baseDir); + if (folderName.includes("__")) { + const [crop, disease] = folderName.split("__"); + + // Clean the disease name by removing leading underscore + const cleanedDisease = disease.startsWith("_") ? disease.slice(1) : disease; + + console.log(`Processing Folder: ${folderName} | Crop: ${crop}, Disease: ${cleanedDisease}`); + + // Read only files in the baseDir folder + const files = fs.readdirSync(baseDir) + .filter(file => file.toLowerCase().endsWith(".jpg") || file.toLowerCase().endsWith(".png")); + + // Get first 50 and last 50 images + const first50 = files.slice(0, 50); + const last50 = files.slice(-50); + const selectedFiles = [...first50, ...last50]; + + for (const file of selectedFiles) { + const filePath = path.join(baseDir, file); + console.log(`Found Image: ${filePath}`); + const imageUrl = await uploadToCloudinary(filePath); + + if (imageUrl) { + const document = { + image_url: imageUrl, + crop, + disease: cleanedDisease, // Use the cleaned disease name + }; + + await collection.insertOne(document); + console.log(`Saved to MongoDB: ${filePath}`); + } else { + console.log(`Failed to upload image: ${filePath}`); + } + } + } else { + console.log(`Skipping folder (invalid format): ${folderName}`); + } + + console.log("Image upload process completed successfully."); + } catch (err) { + console.error(`An error occurred: ${err.message}`); + } finally { + await client.close(); + console.log("MongoDB connection closed."); + } +} + +const BASE_DIR = process.env.BASE_DIR; + +uploadImagesToMongo(BASE_DIR); \ No newline at end of file diff --git a/backdoors/package-lock.json b/backdoors/package-lock.json new file mode 100644 index 0000000..20c6858 --- /dev/null +++ b/backdoors/package-lock.json @@ -0,0 +1,278 @@ +{ + "name": "backdoors", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "axios": "^1.7.9", + "cloudinary": "^2.5.1", + "dotenv": "^16.4.7", + "mongodb": "^6.12.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/bson": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/cloudinary": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.5.1.tgz", + "integrity": "sha512-CNg6uU53Hl4FEVynkTGpt5bQEAQWDHi3H+Sm62FzKf5uQHipSN2v7qVqS8GRVqeb0T1WNV+22+75DOJeRXYeSQ==", + "dependencies": { + "lodash": "^4.17.21", + "q": "^1.5.1" + }, + "engines": { + "node": ">=9" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mongodb": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + } + } +} diff --git a/backdoors/package.json b/backdoors/package.json new file mode 100644 index 0000000..e387838 --- /dev/null +++ b/backdoors/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "axios": "^1.7.9", + "cloudinary": "^2.5.1", + "dotenv": "^16.4.7", + "mongodb": "^6.12.0" + } +} diff --git a/backend/controllers/admin.controller.js b/backend/controllers/admin.controller.js new file mode 100644 index 0000000..b2fd79e --- /dev/null +++ b/backend/controllers/admin.controller.js @@ -0,0 +1,89 @@ +import Image from "../models/image.model.js"; +import Metadata from "../models/metadata.model.js"; +import jwt from "jsonwebtoken"; + +export const getAdminToken = async (req, res) => { + try { + const { password } = req.body; + const adminPassword = process.env.ADMIN_PASSWORD; + + console.log({password, adminPassword}); + + if (password !== adminPassword) { + return res.status(401).json({ error: "Invalid Admin Credentials" }); + } + + const payload = { + adminPassword, + }; + + const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "5h" }); + return res.status(200).json(token); + } catch (error) { + console.log("Error in getting Admin Token", error); + res.status(500).json({ error: "Internal Server error" }); + } +} + +export const getImages = async (req, res) => { + const limit = 200; + const page = parseInt(req.query.page) || 1; + + try { + const totalImages = await Image.countDocuments(); + const images = await Image.find({}) + .sort({ _id: -1 }) + .skip((page - 1) * limit) + .limit(limit); + + res.status(200).json({ + totalImages, + currentPage: page, + totalPages: Math.ceil(totalImages / limit), + images, + }); + } catch (error) { + console.log("Error in getImages admin controller", error); + res.status(500).json({ error: "Internal Server error" }); + } +}; + +export const deleteImage = async (req, res) => { + const { id } = req.params; + + try { + const deletedImage = await Image.findByIdAndDelete(id); + + if (deletedImage) { + res.status(200).json({ + message: "Image deleted successfully", + image: deletedImage, + }); + } else { + res.status(404).json({ error: "Image not found" }); + } + } catch (error) { + console.log("Error in deleteImageById admin controller", error); + res.status(500).json({ error: "Internal Server error" }); + } +}; + +export const createDummyMetaData = async (req, res) => { + try { + const id = req.params.id; + const newMetadata = new Metadata({ + user: id, + totalGP: 500 + }); + + if (newMetadata) { + await newMetadata.save(); + res.status(201).json(newMetadata); + } else { + res.status(400).json({ error: "Invalid data provided for creating metdata" }); + } + } catch (error) { + console.log("Error in creating dummy metadata", error.message); + res.status(500).json({ error: "Internal Server error" }); + } +} \ No newline at end of file diff --git a/backend/index.js b/backend/index.js index 946a696..1820f44 100644 --- a/backend/index.js +++ b/backend/index.js @@ -17,6 +17,7 @@ import elevatedUserRoutes from "./routes/elevatedUser.routes.js"; import analysisRoutes from "./routes/analysis.routes.js"; import marketplaceRoutes from "./routes/marketplace.routes.js"; import paymentRoutes from "./routes/payment.routes.js"; +import adminRoutes from "./routes/admin.routes.js"; const PORT = process.env.PORT || 5000; @@ -57,6 +58,7 @@ app.use("/api/v1/elevatedUser", elevatedUserRoutes); app.use("/api/v1/dashboard", analysisRoutes); app.use("/api/v1/marketplace", marketplaceRoutes); app.use("/api/v1/payment", paymentRoutes); +app.use("/api/v1/admin", adminRoutes); app.listen(PORT, () => { console.log(`Server listening on PORT: ${PORT}`); diff --git a/backend/middlewares/admin.middleware.js b/backend/middlewares/admin.middleware.js new file mode 100644 index 0000000..2cd984b --- /dev/null +++ b/backend/middlewares/admin.middleware.js @@ -0,0 +1,29 @@ +import jwt from "jsonwebtoken"; + +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD; + +const verifyAdmin = (req, res, next) => { + try { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return res.status(401).json({ message: "Unauthorized: No token provided" }); + } + + const token = authHeader.split(" ")[1]; + const decoded = jwt.verify(token, process.env.JWT_SECRET); + if (!decoded || !decoded.adminPassword) { + return res.status(401).json({ message: "Unauthorized: Invalid token" }); + } + + if (decoded.adminPassword !== ADMIN_PASSWORD) { + return res.status(403).json({ message: "Forbidden: Invalid admin password" }); + } + + next(); + } catch (error) { + console.error("Error verifyAdmin middleware", error); + res.status(500).json({ error: "Internal Server Error" }); + } +}; + +export default verifyAdmin; diff --git a/backend/models/image.model.js b/backend/models/image.model.js new file mode 100644 index 0000000..de11af0 --- /dev/null +++ b/backend/models/image.model.js @@ -0,0 +1,20 @@ +import mongoose from "mongoose"; + +const ImageSchema = new mongoose.Schema({ + image_url: { + type: String, + required: true + }, + crop: { + type: String, + required: true + }, + disease: { + type: String, + required: true + }, +}); + +const Image = mongoose.model("Image", ImageSchema); + +export default Image; \ No newline at end of file diff --git a/backend/routes/admin.routes.js b/backend/routes/admin.routes.js new file mode 100644 index 0000000..a6542ea --- /dev/null +++ b/backend/routes/admin.routes.js @@ -0,0 +1,12 @@ +import express from "express"; +import { createDummyMetaData, deleteImage, getAdminToken, getImages } from "../controllers/admin.controller.js"; +import verifyAdmin from "../middlewares/admin.middleware.js"; + +const router = express.Router(); + +router.post("/token", getAdminToken); +router.get("/images", verifyAdmin, getImages); +router.delete("/delete-image/:id", verifyAdmin, deleteImage); +router.post("/dummy-metadata/:id", verifyAdmin, createDummyMetaData); + +export default router; \ No newline at end of file