diff --git a/__tests__/controllers/incidentReportsController.test.js b/__tests__/controllers/incidentReportsController.test.js deleted file mode 100644 index 2e027f2..0000000 --- a/__tests__/controllers/incidentReportsController.test.js +++ /dev/null @@ -1,256 +0,0 @@ -import mongoose from 'mongoose'; -import { MongoMemoryServer } from 'mongodb-memory-server'; -import request from 'supertest'; -import app from '../../index.js'; -import IncidentReports from '../../models/incidentReport.js'; -import User from '../../models/users.js'; -import jwt from 'jsonwebtoken'; - -let mongoServer; -let adminToken; -let verifiedUserToken; -let regularUserToken; -let adminUser; -let verifiedUser; -let regularUser; - -beforeAll(async () => { - mongoServer = await MongoMemoryServer.create(); - const mongoUri = mongoServer.getUri(); - await mongoose.connect(mongoUri); - - // Create users with different roles - adminUser = await User.create({ - name: 'Admin User', - email: 'admin@test.com', - password: 'admin123', - type: 'admin', - verification_status: true - }); - - verifiedUser = await User.create({ - name: 'Verified User', - email: 'verified@test.com', - password: 'verified123', - type: 'verified', - verification_status: true - }); - - regularUser = await User.create({ - name: 'Regular User', - email: 'regular@test.com', - password: 'regular123', - type: 'registered', - verification_status: false - }); - - // Create tokens - adminToken = jwt.sign( - { userId: adminUser._id, type: 'admin' }, - process.env.JWT_SECRET || 'test-secret' - ); - - verifiedUserToken = jwt.sign( - { userId: verifiedUser._id, type: 'verified' }, - process.env.JWT_SECRET || 'test-secret' - ); - - regularUserToken = jwt.sign( - { userId: regularUser._id, type: 'registered' }, - process.env.JWT_SECRET || 'test-secret' - ); -}); - -afterAll(async () => { - await mongoose.disconnect(); - await mongoServer.stop(); -}); - -beforeEach(async () => { - await IncidentReports.deleteMany({}); -}); - -describe('Incident Reports Controller', () => { - const validIncidentData = { - title: 'Test Incident', - disaster_category: 'flood', - description: 'Test description', - location: { - latitude: 12.9716, - longitude: 77.5946, - address: { - city: 'Test City', - district: 'Test District', - province: 'Test Province', - details: 'Test Address Details' - } - }, - date_time: new Date().toISOString(), - severity: 'high', - response_status: 'pending', - images: ['http://example.com/image1.jpg', 'http://example.com/image2.jpg'] - }; - - describe('POST /api/incidentReport', () => { - it('should allow verified user to create incident report', async () => { - const response = await request(app) - .post('/api/incidentReport') - .set('Authorization', `Bearer ${verifiedUserToken}`) - .send(validIncidentData); - - expect(response.status).toBe(201); - expect(response.body.title).toBe(validIncidentData.title); - expect(response.body.user_id.id).toBe(verifiedUser._id.toString()); - }); - - it('should allow admin to create incident report', async () => { - const response = await request(app) - .post('/api/incidentReport') - .set('Authorization', `Bearer ${adminToken}`) - .send(validIncidentData); - - expect(response.status).toBe(201); - expect(response.body.title).toBe(validIncidentData.title); - }); - - it('should not allow regular user to create incident report', async () => { - const response = await request(app) - .post('/api/incidentReport') - .set('Authorization', `Bearer ${regularUserToken}`) - .send(validIncidentData); - - expect(response.status).toBe(403); - }); - - it('should validate incident data', async () => { - const invalidData = { - ...validIncidentData, - disaster_category: 'invalid_category' - }; - - const response = await request(app) - .post('/api/incidentReport') - .set('Authorization', `Bearer ${verifiedUserToken}`) - .send(invalidData); - - expect(response.status).toBe(400); - }); - }); - - describe('GET /api/incidentReport', () => { - beforeEach(async () => { - await IncidentReports.create([ - { - ...validIncidentData, - user_id: verifiedUser._id - }, - { - ...validIncidentData, - disaster_category: 'earthquake', - severity: 'medium', - user_id: verifiedUser._id - } - ]); - }); - - it('should allow any user to get all incident reports', async () => { - const response = await request(app) - .get('/api/incidentReport') - .set('Authorization', `Bearer ${regularUserToken}`); - - expect(response.status).toBe(200); - expect(response.body.reports).toHaveLength(2); - }); - - it('should filter reports by disaster category', async () => { - const response = await request(app) - .get('/api/incidentReport?disaster_category=flood') - .set('Authorization', `Bearer ${regularUserToken}`); - - expect(response.status).toBe(200); - expect(response.body.reports).toHaveLength(1); - expect(response.body.reports[0].disaster_category).toBe('flood'); - }); - - it('should filter reports by location', async () => { - const response = await request(app) - .get('/api/incidentReport?city=Test City') - .set('Authorization', `Bearer ${regularUserToken}`); - - expect(response.status).toBe(200); - expect(response.body.reports.length).toBeGreaterThan(0); - expect(response.body.reports[0].location.address.city).toBe('Test City'); - }); - }); - - describe('GET /api/incidentReport/nearby', () => { - beforeEach(async () => { - await IncidentReports.create({ - ...validIncidentData, - user_id: verifiedUser._id // Add the required user_id - }); - }); - - it('should get nearby incidents', async () => { - const response = await request(app) - .get('/api/incidentReport/nearby') - .query({ - latitude: validIncidentData.location.latitude, - longitude: validIncidentData.location.longitude, - maxDistance: 1000 - }) - .set('Authorization', `Bearer ${regularUserToken}`); - - expect(response.status).toBe(200); - expect(Array.isArray(response.body)).toBe(true); - }); - }); - - describe('PUT /api/incidentReport/:id', () => { - let testIncident; - - beforeEach(async () => { - testIncident = await IncidentReports.create({ - ...validIncidentData, - user_id: verifiedUser._id - }); - }); - - it('should allow verified user to update their own report', async () => { - const updateData = { - ...validIncidentData, // Include all required fields - severity: 'medium', - description: 'Updated description' - }; - - const response = await request(app) - .put(`/api/incidentReport/${testIncident._id}`) - .set('Authorization', `Bearer ${verifiedUserToken}`) - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.severity).toBe('medium'); - }); - }); - - describe('DELETE /api/incidentReport/:id', () => { - let testIncident; - - beforeEach(async () => { - testIncident = await IncidentReports.create({ - ...validIncidentData, - user_id: verifiedUser._id - }); - }); - - it('should allow verified user to delete their own report', async () => { - const response = await request(app) - .delete(`/api/incidentReport/${testIncident._id}`) - .set('Authorization', `Bearer ${verifiedUserToken}`); - - expect(response.status).toBe(200); - const deletedReport = await IncidentReports.findById(testIncident._id); - expect(deletedReport).toBeNull(); - }); - }); -}); \ No newline at end of file diff --git a/controllers/incidentReportsController.js b/controllers/incidentReportsController.js deleted file mode 100644 index 80e2640..0000000 --- a/controllers/incidentReportsController.js +++ /dev/null @@ -1,263 +0,0 @@ -import IncidentReports from "../models/incidentReport.js"; -import { createSystemLog } from "./adminLogsController.js"; - -export const createIncidentReport = async (req, res) => { - try { - // Check authorization - if (req.user.type !== "admin" && req.user.type !== "verified") { - return res.status(403).json({ - message: "Only admin and verified users can create incident reports", - }); - } - - const { - title, - disaster_category, - description, - location, - date_time, - severity, - response_status, - images, - } = req.body; - - // Create the report - const newReport = await IncidentReports.create({ - title, - disaster_category, - description, - location: { - latitude: location.latitude, - longitude: location.longitude, - address: { - city: location.address.city, - district: location.address.district, - province: location.address.province, - details: location.address.details, - }, - }, - date_time, - user_id: req.user.id, - severity, - response_status, - images, - verified_by: [req.user.id], // Initial verification by creator - }); - - // Create system log - await createSystemLog( - req.user.id, - "CREATE_INCIDENT_REPORT", - "incident_report", - newReport._id, // Changed from report._id to newReport._id - { - new_state: newReport.toObject(), - message: `New incident report ${newReport.title} was created`, - }, - ); - - // Populate and return the response - const populatedReport = await newReport.populate("user_id", "name email"); - res.status(201).json(populatedReport); - } catch (error) { - console.log("Create Incident Report Error:", error); - res.status(400).json({ error: error.message }); - } -}; - -export const getIncidentReports = async (req, res) => { - try { - const { - disaster_category, - severity, - response_status, - city, - district, - province, - startDate, - endDate, - limit = 10, - page = 1, - } = req.query; - - // Build query - const query = {}; - - // Add filters if they exist - if (disaster_category) query.disaster_category = disaster_category; - if (severity) query.severity = severity; - if (response_status) query.response_status = response_status; - - // Location filters - if (city || district || province) { - if (city) - query["location.address.city"] = { $regex: city, $options: "i" }; - if (district) - query["location.address.district"] = { - $regex: district, - $options: "i", - }; - if (province) - query["location.address.province"] = { - $regex: province, - $options: "i", - }; - } - - // Date range filter - if (startDate || endDate) { - query.date_time = {}; - if (startDate) query.date_time.$gte = new Date(startDate); - if (endDate) query.date_time.$lte = new Date(endDate); - } - - // Pagination - const skip = (page - 1) * limit; - - const reports = await IncidentReports.find(query) - .populate("user_id", "name email") - .populate("verified_by", "name email") - .sort({ date_time: -1 }) - .limit(parseInt(limit)) - .skip(skip); - - const total = await IncidentReports.countDocuments(query); - - res.status(200).json({ - reports, - currentPage: parseInt(page), - totalPages: Math.ceil(total / parseInt(limit)), - totalReports: total, - }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}; - -// Get reports near a location -export const getNearbyIncidents = async (req, res) => { - try { - const { latitude, longitude, maxDistance = 10000 } = req.query; // maxDistance in meters - - if (!latitude || !longitude) { - return res - .status(400) - .json({ message: "Latitude and longitude are required" }); - } - - const reports = await IncidentReports.find({ - location: { - $near: { - $geometry: { - type: "Point", - coordinates: [parseFloat(longitude), parseFloat(latitude)], - }, - $maxDistance: parseInt(maxDistance), - }, - }, - }).populate("user_id", "name email"); - - res.status(200).json(reports); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}; - -export const getIncidentReportById = async (req, res) => { - try { - const report = await IncidentReports.findById(req.params.id) - .populate("user_id", "name email") - .populate("verified_by", "name email"); - - if (!report) - return res.status(404).json({ message: "Incident report not found" }); - - res.status(200).json(report); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}; - -export const updateIncidentReport = async (req, res) => { - try { - const report = await IncidentReports.findById(req.params.id); - if (!report) { - return res.status(404).json({ message: "Incident report not found" }); - } - - // Check authorization - if ( - req.user.type !== "admin" && - report.user_id.toString() !== req.user.id - ) { - return res - .status(403) - .json({ message: "Not authorized to update this report" }); - } - - const originalReport = report.toObject(); - const updatedReport = await IncidentReports.findByIdAndUpdate( - req.params.id, - { ...req.body }, - { new: true, runValidators: true }, - ) - .populate("user_id", "name email") - .populate("verified_by", "name email"); - - if (typeof createSystemLog === "function") { - await createSystemLog( - req.user.id, - "UPDATE_INCIDENT_REPORT", - "incident_report", - updatedReport._id, - { - previous_state: originalReport, - new_state: updatedReport.toObject(), - message: `Incident report ${updatedReport.title} was updated`, - }, - ); - } - - res.status(200).json(updatedReport); - } catch (error) { - res.status(400).json({ error: error.message }); - } -}; - -export const deleteIncidentReport = async (req, res) => { - try { - const report = await IncidentReports.findById(req.params.id); - if (!report) { - return res.status(404).json({ message: "Incident report not found" }); - } - - // Check authorization - if ( - req.user.type !== "admin" && - report.user_id.toString() !== req.user.id - ) { - return res - .status(403) - .json({ message: "Not authorized to delete this report" }); - } - - await IncidentReports.findByIdAndDelete(req.params.id); - - if (typeof createSystemLog === "function") { - await createSystemLog( - req.user.id, - "DELETE_INCIDENT_REPORT", - "incident_report", - report._id, - { - previous_state: report.toObject(), - message: `Incident report ${report.title} was deleted`, - }, - ); - } - - res.status(200).json({ message: "Incident report deleted successfully" }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}; diff --git a/controllers/resourceController.js b/controllers/resourceController.js index ad69fde..02c62cd 100644 --- a/controllers/resourceController.js +++ b/controllers/resourceController.js @@ -12,6 +12,11 @@ export const createResource = async (req, res) => { contact, availability_status, content, + metadata, + tags, + operating_hours, + capacity, + emergency_level, } = req.body; // Create base resource data @@ -21,6 +26,9 @@ export const createResource = async (req, res) => { type, contact, added_by: req.user.id, + status: "active", + tags: tags || [], + metadata: metadata || {}, }; // Add category-specific fields @@ -48,6 +56,10 @@ export const createResource = async (req, res) => { resourceData.location = locationData; resourceData.availability_status = availability_status; + resourceData.operating_hours = operating_hours; + if (type === "shelter") { + resourceData.capacity = capacity; + } } if (category === "guide") { @@ -55,12 +67,15 @@ export const createResource = async (req, res) => { resourceData.content = content; } + if (category === "emergency_contact") { + resourceData.emergency_level = emergency_level; + } + const resource = new Resource(resourceData); const savedResource = await resource.save(); - await createSystemLog( req.user.id, - "CREATE_RESOURCE", // Instead of 'CREATE' + "CREATE_RESOURCE", "resource", resource._id, { @@ -89,14 +104,20 @@ export const getFacilities = async (req, res) => { city, district, province, + status, + tags, limit = 10, page = 1, } = req.query; - const query = { category: "facility" }; + const query = { + category: "facility", + status: status || "active", + }; if (type) query.type = type; if (availability_status) query.availability_status = availability_status; + if (tags) query.tags = { $in: tags.split(",") }; if (city || district || province) { if (city) @@ -117,6 +138,7 @@ export const getFacilities = async (req, res) => { const resources = await Resource.find(query) .populate("added_by", "name email") + .sort({ last_verified: -1 }) .limit(parseInt(limit)) .skip(skip); @@ -136,15 +158,21 @@ export const getFacilities = async (req, res) => { export const getGuides = async (req, res) => { try { - const { type, limit = 10, page = 1 } = req.query; + const { type, tags, limit = 10, page = 1 } = req.query; + + const query = { + category: "guide", + status: "active", + }; - const query = { category: "guide" }; if (type) query.type = type; + if (tags) query.tags = { $in: tags.split(",") }; const skip = (page - 1) * limit; const resources = await Resource.find(query) .populate("added_by", "name email") + .sort({ last_verified: -1 }) .limit(parseInt(limit)) .skip(skip); @@ -164,9 +192,18 @@ export const getGuides = async (req, res) => { export const getEmergencyContacts = async (req, res) => { try { - const resources = await Resource.find({ + const { emergency_level } = req.query; + + const query = { category: "emergency_contact", - }).populate("added_by", "name email"); + status: "active", + }; + + if (emergency_level) query.emergency_level = emergency_level; + + const resources = await Resource.find(query) + .populate("added_by", "name email") + .sort({ emergency_level: -1 }); res.status(200).json({ success: true, @@ -179,37 +216,34 @@ export const getEmergencyContacts = async (req, res) => { export const getNearbyFacilities = async (req, res) => { try { - const { latitude, longitude, maxDistance = 10000, type } = req.query; + const { latitude, longitude, maxDistance = 10000, type, availability_status } = req.query; if (!latitude || !longitude) { - return res - .status(400) - .json({ message: "Latitude and longitude are required" }); + return res.status(400).json({ message: "Latitude and longitude are required" }); } const query = { - category: "facility", - "location.coordinates": { - $nearSphere: { + category: 'facility', + status: 'active', + 'location.coordinates': { + $near: { $geometry: { - type: "Point", - coordinates: [parseFloat(longitude), parseFloat(latitude)], + type: 'Point', + coordinates: [parseFloat(longitude), parseFloat(latitude)] }, - $maxDistance: parseFloat(maxDistance), - }, - }, + $maxDistance: parseFloat(maxDistance) + } + } }; if (type) query.type = type; + if (availability_status) query.availability_status = availability_status; - const resources = await Resource.find(query).populate( - "added_by", - "name email", - ); + const resources = await Resource.find(query).populate("added_by", "name email"); res.status(200).json({ success: true, - data: resources, + data: resources }); } catch (error) { res.status(500).json({ error: error.message }); @@ -244,14 +278,14 @@ export const updateResource = async (req, res) => { } // Check if user is authorized to update - if ( - req.user.type !== "admin" && - originalResource.added_by.toString() !== req.user.id - ) { - return res - .status(403) - .json({ message: "Not authorized to update this resource" }); - } + // if ( + // req.user.type !== "admin" && + // originalResource.added_by.toString() !== req.user.id + // ) { + // return res + // .status(403) + // .json({ message: "Not authorized to update this resource" }); + // } const updatedResource = await Resource.findByIdAndUpdate( req.params.id, @@ -288,14 +322,14 @@ export const deleteResource = async (req, res) => { } // Check if user is authorized to delete - if ( - req.user.type !== "admin" && - resource.added_by.toString() !== req.user.id - ) { - return res - .status(403) - .json({ message: "Not authorized to delete this resource" }); - } + // if ( + // req.user.type !== "admin" && + // resource.added_by.toString() !== req.user.id + // ) { + // return res + // .status(403) + // .json({ message: "Not authorized to delete this resource" }); + // } await Resource.findByIdAndDelete(req.params.id); diff --git a/controllers/subscriptionController.js b/controllers/subscriptionController.js deleted file mode 100644 index 4ce3f86..0000000 --- a/controllers/subscriptionController.js +++ /dev/null @@ -1,70 +0,0 @@ -import Subscription from "../models/subscriptions.js"; - -export const createSubscription = async (req, res) => { - try { - // Check if user already has a subscription - const existingSubscription = await Subscription.findOne({ - user_id: req.user.id, - }); - if (existingSubscription) { - return res.status(400).json({ - message: "User already has an active subscription", - }); - } - - const subscription = await Subscription.create({ - ...req.body, - user_id: req.user.id, - }); - - res.status(201).json(subscription); - } catch (error) { - res.status(400).json({ error: error.message }); - } -}; - -export const getMySubscription = async (req, res) => { - try { - const subscription = await Subscription.findOne({ user_id: req.user.id }); - if (!subscription) { - return res.status(404).json({ message: "No subscription found" }); - } - res.status(200).json(subscription); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}; - -export const updateMySubscription = async (req, res) => { - try { - const subscription = await Subscription.findOneAndUpdate( - { user_id: req.user.id }, - req.body, - { new: true, runValidators: true }, - ); - - if (!subscription) { - return res.status(404).json({ message: "No subscription found" }); - } - - res.status(200).json(subscription); - } catch (error) { - res.status(400).json({ error: error.message }); - } -}; - -export const deleteMySubscription = async (req, res) => { - try { - const subscription = await Subscription.findOneAndDelete({ - user_id: req.user.id, - }); - - if (!subscription) { - return res.status(404).json({ message: "No subscription found" }); - } - - res.status(200).json({ message: "Subscription deleted successfully" }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}; diff --git a/controllers/userController.js b/controllers/userController.js index 8e70cfb..ab4c4cd 100644 --- a/controllers/userController.js +++ b/controllers/userController.js @@ -15,19 +15,17 @@ export const createUser = async (req, res) => { }); } - const existingUser = await User.findOne({ - $or: [ - { email: email.toLowerCase() }, - { workId: workId } - ] + const existingUser = await User.findOne({ + $or: [{ email: email.toLowerCase() }, { workId: workId }], }); - + if (existingUser) { return res.status(409).json({ success: false, - message: existingUser.email === email.toLowerCase() - ? "Email already registered." - : "Work ID already registered.", + message: + existingUser.email === email.toLowerCase() + ? "Email already registered." + : "Work ID already registered.", }); } @@ -63,7 +61,7 @@ export const getAllUsers = async (req, res) => { const query = {}; if (department) query.associated_department = department; if (email) query.email = new RegExp(email, "i"); - if (isVerified !== undefined) query.isVerified = isVerified === 'true'; + if (isVerified !== undefined) query.isVerified = isVerified === "true"; const users = await User.find(query) .skip((page - 1) * limit) @@ -200,12 +198,13 @@ export const authenticateUser = async (req, res) => { }); } - if (!user.isVerified) { - return res.status(401).json({ - success: false, - message: "Account not verified. Please wait for verification.", - }); - } + // Holding this because verification process need tobe implemented + // if (!user.isVerified) { + // return res.status(401).json({ + // success: false, + // message: "Account not verified. Please wait for verification.", + // }); + // } const isMatch = await user.comparePassword(password); if (!isMatch) { @@ -220,7 +219,7 @@ export const authenticateUser = async (req, res) => { await user.save(); const token = jwt.sign( - { userId: user._id }, + { userId: user._id, isVerified: user.isVerified }, process.env.JWT_SECRET, { expiresIn: "24h" }, ); @@ -241,7 +240,7 @@ export const authenticateUser = async (req, res) => { name: user.name, email: user.email, department: user.associated_department, - isVerified: user.isVerified + isVerified: user.isVerified, }, }); } catch (error) { diff --git a/controllers/userReportController.js b/controllers/userReportController.js index 9a0d4bc..ff3ce46 100644 --- a/controllers/userReportController.js +++ b/controllers/userReportController.js @@ -1,21 +1,190 @@ import UserReports from "../models/userReports.js"; +import User from "../models/users.js"; +import Warning from "../models/warning.js"; import { createSystemLog } from "./adminLogsController.js"; export const createUserReport = async (req, res) => { try { - const report = { + const { title, disaster_category, description, location } = req.body; + const user = req.user; + + if (!title || !disaster_category || !description || !location) { + return res.status(400).json({ + error: + "Missing required fields: title, disaster_category, description, and location are required", + }); + } + + if ( + !location.address || + !location.address.city || + !location.address.district || + !location.address.province + ) { + return res.status(400).json({ + error: + "Location must include address with city, district, and province", + }); + } + + const validCategories = [ + "flood", + "fire", + "earthquake", + "landslide", + "cyclone", + ]; + if (!validCategories.includes(disaster_category)) { + return res.status(400).json({ + error: "Invalid disaster category", + }); + } + + if (req.body.images) { + const invalidImages = req.body.images.filter( + (url) => !url.startsWith("http"), + ); + if (invalidImages.length > 0) { + return res.status(400).json({ + error: "All images must be valid URLs starting with 'http'", + }); + } + } + + const reportData = { ...req.body, - user_id: req.user.id, - status: "pending", + verification_status: "pending", + reporter_type: "anonymous", }; - if (!report.location.latitude && !report.location.longitude) { + if (user) { + reportData.reporter = user._id; + reportData.reporter_type = "registered"; + if (user.isVerified) { + reportData.verification_status = "verified"; + reportData.verification = { + verified_by: user._id, + verified_at: new Date(), + severity: "medium", + }; + } + } + + const newReport = await UserReports.create(reportData); + + if (user?.isVerified) { + await createSystemLog( + user._id, + "AUTO_VERIFY_REPORT", + "user_report", + newReport._id, + { + message: `Report auto-verified by verified user ${user.name}`, + }, + ); } - const newReport = await UserReports.create(report); res.status(201).json(newReport); } catch (error) { - res.status(400).json({ error: error.message }); + res.status(500).json({ error: error.message }); + } +}; + +export const verifyReport = async (req, res) => { + try { + const { severity, notes } = req.body; + const reportId = req.params.id; + const verifyingUser = req.user; + + if (!verifyingUser.isVerified) { + return res.status(403).json({ + error: "Only verified users can verify reports", + }); + } + + const report = await UserReports.findById(reportId); + if (!report) { + return res.status(404).json({ error: "Report not found" }); + } + + const verificationTime = Math.round( + (new Date() - report.createdAt) / (1000 * 60), + ); + + report.verification_status = "verified"; + report.verification = { + verified_by: verifyingUser._id, + verified_at: new Date(), + workId: verifyingUser.workId, + associated_department: verifyingUser.associated_department, + verification_time: verificationTime, + severity, + notes, + }; + + const updatedReport = await report.save(); + + await createSystemLog( + verifyingUser._id, + "VERIFY_REPORT", + "user_report", + report._id, + { + previous_state: report.toJSON(), + new_state: updatedReport.toJSON(), + message: `Report verified by ${verifyingUser.name} with ${severity} severity`, + }, + ); + + res.status(200).json(updatedReport); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +export const dismissReport = async (req, res) => { + try { + const { notes } = req.body; + const reportId = req.params.id; + const user = req.user; + + if (!user.isVerified) { + return res.status(403).json({ + error: "Only verified users can dismiss reports", + }); + } + + const report = await UserReports.findById(reportId); + if (!report) { + return res.status(404).json({ error: "Report not found" }); + } + + const originalState = report.toJSON(); + + report.verification_status = "dismissed"; + report.verification = { + verified_by: user._id, + verified_at: new Date(), + notes, + }; + + const updatedReport = await report.save(); + + await createSystemLog( + user._id, + "DISMISS_REPORT", + "user_report", + report._id, + { + previous_state: originalState, + new_state: updatedReport.toJSON(), + message: `Report dismissed by ${user.name}`, + }, + ); + + res.status(200).json(updatedReport); + } catch (error) { + res.status(500).json({ error: error.message }); } }; @@ -28,7 +197,9 @@ export const getUserReports = async (req, res) => { province, startDate, endDate, - status, + verification_status, + severity, + reporter_type, limit = 10, page = 1, } = req.query; @@ -36,7 +207,9 @@ export const getUserReports = async (req, res) => { const query = {}; if (disaster_category) query.disaster_category = disaster_category; - if (status) query.status = status; + if (verification_status) query.verification_status = verification_status; + if (reporter_type) query.reporter_type = reporter_type; + if (severity) query["verification.severity"] = severity; if (city || district || province) { if (city) @@ -62,7 +235,8 @@ export const getUserReports = async (req, res) => { const skip = (page - 1) * limit; const reports = await UserReports.find(query) - .populate("user_id", "name email") + .populate("reporter", "name workId associated_department") + .populate("verification.verified_by", "name workId associated_department") .sort({ date_time: -1 }) .limit(parseInt(limit)) .skip(skip); @@ -80,98 +254,437 @@ export const getUserReports = async (req, res) => { } }; -export const getUserReportById = async (req, res) => { +export const getReportsByUser = async (req, res) => { try { - const report = await UserReports.findById(req.params.id).populate( - "user_id", - "name email", - ); + const userId = req.user._id; + const { status, page = 1, limit = 10 } = req.query; + + const query = { reporter: userId }; + if (status) query.verification_status = status; - if (!report) return res.status(404).json({ message: "Report not found" }); - res.status(200).json(report); + const reports = await UserReports.find(query) + .populate("verification.verified_by", "name workId associated_department") + .sort({ createdAt: -1 }) + .limit(parseInt(limit)) + .skip((page - 1) * limit); + + const total = await UserReports.countDocuments(query); + + res.status(200).json({ + reports, + currentPage: parseInt(page), + totalPages: Math.ceil(total / parseInt(limit)), + totalReports: total, + }); } catch (error) { res.status(500).json({ error: error.message }); } }; -export const updateUserReport = async (req, res) => { +export const getReportStats = async (req, res) => { try { - const originalReport = await UserReports.findById(req.params.id); - - if (!originalReport) { - return res.status(404).json({ message: "Report not found" }); - } + const stats = await UserReports.aggregate([ + { + $facet: { + byStatus: [ + { + $group: { + _id: "$verification_status", + count: { $sum: 1 }, + }, + }, + ], + byCategory: [ + { + $group: { + _id: "$disaster_category", + count: { $sum: 1 }, + }, + }, + ], + bySeverity: [ + { $match: { verification_status: "verified" } }, + { + $group: { + _id: "$verification.severity", + count: { $sum: 1 }, + }, + }, + ], + recentTrends: [ + { + $match: { + date_time: { + $gte: new Date(new Date().setDate(new Date().getDate() - 30)), + }, + }, + }, + { + $group: { + _id: { + $dateToString: { + format: "%Y-%m-%d", + date: "$date_time", + }, + }, + count: { $sum: 1 }, + }, + }, + ], + }, + }, + ]); - // Only allow update if user is the owner or an admin - if ( - originalReport.user_id.toString() !== req.user.id && - req.user.type !== "admin" - ) { - return res - .status(403) - .json({ message: "Not authorized to update this report" }); - } + res.status(200).json(stats[0]); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; - const updatedReport = await UserReports.findByIdAndUpdate( - req.params.id, - req.body, - { new: true, runValidators: true }, - ).populate("user_id", "name email"); +export const getVerificationStats = async (req, res) => { + try { + const today = new Date(); + today.setHours(0, 0, 0, 0); - if (req.user.type === "admin") { - await createSystemLog( - req.user.id, - "UPDATE_USER_REPORT", - "user_report", - updatedReport._id, + const stats = await Promise.all([ + // Pending reports count + UserReports.countDocuments({ verification_status: "pending" }), + + // Verified today count + UserReports.countDocuments({ + verification_status: "verified", + "verification.verified_at": { $gte: today } + }), + + // Active incidents count + Warning.countDocuments({ status: "active" }), + + // Average verification time + UserReports.aggregate([ { - previous_state: originalReport.toObject(), - new_state: updatedReport.toObject(), - message: `User report ${updatedReport.title} was updated`, + $match: { + verification_status: "verified", + "verification.verification_time": { $exists: true } + } }, - ); - } + { + $group: { + _id: null, + avgTime: { + $avg: { + $divide: ["$verification.verification_time", 60] // Convert minutes to hours + } + } + } + } + ]) + ]); + + res.json({ + pendingCount: stats[0], + verifiedToday: stats[1], + activeIncidents: stats[2], + avgVerificationTime: Math.round(stats[3][0]?.avgTime || 0) + }); - res.status(200).json(updatedReport); } catch (error) { - res.status(400).json({ error: error.message }); + console.error("Verification stats error:", error); + res.status(500).json({ error: error.message }); } }; -export const deleteUserReport = async (req, res) => { +// Get report analytics +export const getReportAnalytics = async (req, res) => { try { - const report = await UserReports.findById(req.params.id); + const weekAgo = new Date(); + weekAgo.setDate(weekAgo.getDate() - 7); - if (!report) { - return res.status(404).json({ message: "Report not found" }); + const analytics = await UserReports.aggregate([ + { + $facet: { + weeklyTrends: [ + { + $match: { + createdAt: { $gte: weekAgo } + } + }, + { + $group: { + _id: { + date: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } }, + status: "$verification_status" + }, + count: { $sum: 1 } + } + }, + { + $project: { + _id: 0, + date: "$_id.date", + status: "$_id.status", + count: 1 + } + } + ], + reportTypes: [ + { + $group: { + _id: "$disaster_category", + count: { $sum: 1 } + } + }, + { + $project: { + name: { $ifNull: ["$_id", "Unknown"] }, + value: "$count", + _id: 0 + } + } + ], + responseTime: [ + { + $match: { + verification_status: "verified", + "verification.verification_time": { $exists: true } + } + }, + { + $bucket: { + groupBy: "$verification.verification_time", + boundaries: [0, 60, 120, 240, Infinity], + default: "other", + output: { + count: { $sum: 1 } + } + } + }, + { + $project: { + time: { + $switch: { + branches: [ + { case: { $eq: ["$_id", 0] }, then: "<1h" }, + { case: { $eq: ["$_id", 60] }, then: "1-2h" }, + { case: { $eq: ["$_id", 120] }, then: "2-4h" }, + { case: { $eq: ["$_id", 240] }, then: ">4h" } + ], + default: "other" + } + }, + count: 1, + _id: 0 + } + } + ] + } + } + ]); + + // Format weekly trends data + const trendsMap = {}; + analytics[0].weeklyTrends.forEach(item => { + if (!trendsMap[item.date]) { + trendsMap[item.date] = { date: item.date }; + } + trendsMap[item.date][item.status] = item.count; + }); + + const formattedResponse = { + weeklyTrends: Object.values(trendsMap), + reportTypes: analytics[0].reportTypes, + responseTime: analytics[0].responseTime + }; + + res.json(formattedResponse); + } catch (error) { + console.error("Analytics error:", error); + res.status(500).json({ error: error.message }); + } +}; + +// Get public feed of verified reports +export const getPublicFeed = async (req, res) => { + try { + const { page = 1, limit = 10 } = req.query; + + const reports = await UserReports.find({ + verification_status: "verified", + }) + .sort({ "verification.verified_at": -1 }) + .limit(parseInt(limit)) + .skip((page - 1) * parseInt(limit)) + .select("-reporter"); // Exclude sensitive info + + const total = await UserReports.countDocuments({ + verification_status: "verified", + }); + + res.json({ + reports, + currentPage: parseInt(page), + totalPages: Math.ceil(total / parseInt(limit)), + total, + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +// Get feed reports with filters +export const getFeedReports = async (req, res) => { + try { + const { + page = 1, + limit = 10, + disaster_category, + verified_only, + } = req.query; + + // Build query object + const query = {}; + + // Add filters + if (verified_only === "true") { + query.verification_status = "verified"; } - // Only allow deletion if user is the owner or an admin - if ( - report.user_id.toString() !== req.user.id && - req.user.type !== "admin" - ) { - return res - .status(403) - .json({ message: "Not authorized to delete this report" }); + if (disaster_category) { + query.disaster_category = disaster_category; } - if (req.user.type === "admin") { - await createSystemLog( - req.user.id, - "DELETE_USER_REPORT", - "user_report", - report._id, - { - previous_state: report.toObject(), - message: `User report ${report.title} was deleted`, + // Get reports + const reports = await UserReports.find(query) + .sort({ date_time: -1 }) + .skip((parseInt(page) - 1) * parseInt(limit)) + .limit(parseInt(limit)) + .lean(); + + // Get total count for pagination + const total = await UserReports.countDocuments(query); + + res.status(200).json({ + success: true, + data: { + reports: reports.map((report) => ({ + id: report._id, + title: report.title, + description: report.description, + disaster_category: report.disaster_category, + location: report.location, + date_time: report.date_time, + images: report.images, + verification_status: report.verification_status, + verified: report.verification_status === "verified", + severity: report.verification?.severity, + })), + pagination: { + currentPage: parseInt(page), + totalPages: Math.ceil(total / parseInt(limit)), + totalReports: total, }, - ); - } + }, + }); + } catch (error) { + console.error("Feed reports error:", error); + res.status(500).json({ + success: false, + error: "Failed to fetch feed reports", + details: error.message, + }); + } +}; + +// Get feed statistics +export const getFeedStats = async (req, res) => { + try { + // Get disaster type distribution from reports + const reportStats = await UserReports.aggregate([ + { + $group: { + _id: "$disaster_category", + total: { $sum: 1 }, + verified: { + $sum: { + $cond: [{ $eq: ["$verification_status", "verified"] }, 1, 0], + }, + }, + }, + }, + ]); + + // Get active warnings distribution + const warningStats = await Warning.aggregate([ + { + $group: { + _id: "$disaster_category", + active_warnings: { + $sum: { $cond: [{ $eq: ["$status", "active"] }, 1, 0] }, + }, + by_severity: { + $push: { + severity: "$severity", + status: "$status", + }, + }, + }, + }, + ]); - await report.deleteOne(); - res.status(200).json({ message: "Report deleted successfully" }); + res.status(200).json({ + success: true, + data: { + reportStats, + warningStats, + activeWarnings: warningStats.reduce( + (acc, curr) => acc + curr.active_warnings, + 0 + ), + }, + }); } catch (error) { - res.status(500).json({ error: error.message }); + console.error("Feed stats error:", error); + res.status(500).json({ + success: false, + error: "Failed to fetch feed statistics", + details: error.message, + }); + } +}; + +// Get recent updates for live feed +export const getFeedUpdates = async (req, res) => { + try { + const { minutes = 30 } = req.query; + const since = new Date(Date.now() - minutes * 60 * 1000); + + const updates = await UserReports.find({ + date_time: { $gte: since }, + }) + .sort({ date_time: -1 }) + .limit(20) + .select( + "title date_time location.address.city verification_status verification.severity", + ) + .lean(); + + // Modified response structure to match frontend expectations + res.status(200).json({ + success: true, + data: { + updates: updates.map((update) => ({ + message: `${update.title} reported in ${update.location.address.city}`, + timestamp: update.date_time, + status: update.verification_status, + severity: update.verification?.severity, + })), + }, + }); + } catch (error) { + console.error("Feed updates error:", error); + res.status(500).json({ + success: false, + error: "Failed to fetch feed updates", + details: error.message, + }); } }; diff --git a/controllers/warningController.js b/controllers/warningController.js new file mode 100644 index 0000000..174ba7b --- /dev/null +++ b/controllers/warningController.js @@ -0,0 +1,371 @@ +import Warning from "../models/warning.js"; +import { createSystemLog } from "./adminLogsController.js"; + +// Create a new warning +export const createWarning = async (req, res) => { + try { + const { + title, + disaster_category, + description, + affected_locations, + severity, + created_by, + } = req.body; + + if (!created_by) { + return res.status(401).json({ + error: "User ID is required to create warnings", + }); + } + + if ( + !title || + !disaster_category || + !description || + !affected_locations || + !severity + ) { + return res.status(400).json({ + error: + "Missing required fields: title, disaster_category, description, affected_locations, and severity are required", + }); + } + + // Validate locations + const invalidLocations = affected_locations.filter( + (location) => + !location.address?.city || + !location.address?.district || + !location.address?.province, + ); + + if (invalidLocations.length > 0) { + return res.status(400).json({ + error: + "All locations must include address with city, district, and province", + }); + } + + const validCategories = [ + "flood", + "fire", + "earthquake", + "landslide", + "cyclone", + ]; + if (!validCategories.includes(disaster_category)) { + return res.status(400).json({ + error: "Invalid disaster category", + }); + } + + if (req.body.images) { + const invalidImages = req.body.images.filter( + (url) => !url.startsWith("http"), + ); + if (invalidImages.length > 0) { + return res.status(400).json({ + error: "All images must be valid URLs starting with 'http'", + }); + } + } + + const warningData = { + ...req.body, + status: "active", + }; + + const newWarning = await Warning.create(warningData); + + await createSystemLog( + created_by, + "CREATE_WARNING", + "warning", + newWarning._id, + { + message: `New warning created with ID ${newWarning._id}`, + }, + ); + + res.status(201).json(newWarning); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +// Add update to existing warning +export const addWarningUpdate = async (req, res) => { + try { + const { update_text, severity_change } = req.body; + const warningId = req.params.id; + + if (!update_text) { + return res.status(400).json({ + error: "Update text is required", + }); + } + + const warning = await Warning.findById(warningId); + if (!warning) { + return res.status(404).json({ error: "Warning not found" }); + } + + if (warning.status === "resolved") { + return res.status(400).json({ + error: "Cannot update a resolved warning", + }); + } + + const updateData = { + update_text, + updated_at: new Date() + }; + + if (severity_change && ['low', 'medium', 'high', 'critical'].includes(severity_change)) { + updateData.severity_change = severity_change; + warning.severity = severity_change; + } + + warning.updates.push(updateData); + const updatedWarning = await warning.save(); + + res.status(200).json(updatedWarning); + } catch (error) { + console.error('Error in addWarningUpdate:', error); + res.status(500).json({ error: error.message }); + } +}; + +// Add response action to warning +export const addResponseAction = async (req, res) => { + try { + const { action_type, description } = req.body; + const warningId = req.params.id; + const user = req.user; + + if (!action_type || !description) { + return res.status(400).json({ + error: "Action type and description are required", + }); + } + + const warning = await Warning.findById(warningId); + if (!warning) { + return res.status(404).json({ error: "Warning not found" }); + } + + if (warning.status === "resolved") { + return res.status(400).json({ + error: "Cannot add actions to a resolved warning", + }); + } + + const actionData = { + action_type, + description, + performed_by: user._id, + performed_at: new Date(), + status: "planned", + }; + + warning.response_actions.push(actionData); + const updatedWarning = await warning.save(); + + await createSystemLog( + user._id, + "ADD_WARNING_ACTION", + "warning", + warning._id, + { + message: `Response action added by ${user.name}`, + action_details: actionData, + }, + ); + + res.status(200).json(updatedWarning); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +// Update response action status +export const updateActionStatus = async (req, res) => { + try { + const { actionId } = req.params; + const { status } = req.body; + const warningId = req.params.id; + const user = req.user; + + const warning = await Warning.findById(warningId); + if (!warning) { + return res.status(404).json({ error: "Warning not found" }); + } + + const action = warning.response_actions.id(actionId); + if (!action) { + return res.status(404).json({ error: "Action not found" }); + } + + action.status = status; + const updatedWarning = await warning.save(); + + await createSystemLog( + user._id, + "UPDATE_ACTION_STATUS", + "warning", + warning._id, + { + message: `Action status updated by ${user.name}`, + action_id: actionId, + new_status: status, + }, + ); + + res.status(200).json(updatedWarning); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +// Resolve warning +export const resolveWarning = async (req, res) => { + try { + const { resolution_notes } = req.body; + const warningId = req.params.id; + const user = req.user; + + if (!resolution_notes) { + return res.status(400).json({ + error: "Resolution notes are required", + }); + } + + const warning = await Warning.findById(warningId); + if (!warning) { + return res.status(404).json({ error: "Warning not found" }); + } + + warning.status = "resolved"; + warning.resolved_by = user._id; + warning.resolved_at = new Date(); + warning.resolution_notes = resolution_notes; + + const updatedWarning = await warning.save(); + + await createSystemLog(user._id, "RESOLVE_WARNING", "warning", warning._id, { + message: `Warning resolved by ${user.name}`, + resolution_notes, + }); + + res.status(200).json(updatedWarning); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +// Get warnings with filters +export const getWarnings = async (req, res) => { + try { + const { + disaster_category, + status, + severity, + city, + district, + province, + limit = 10, + page = 1, + } = req.query; + + const query = {}; + + if (disaster_category) query.disaster_category = disaster_category; + if (status) query.status = status; + if (severity) query.severity = severity; + + if (city || district || province) { + if (city) + query["affected_locations.address.city"] = { + $regex: city, + $options: "i", + }; + if (district) + query["affected_locations.address.district"] = { + $regex: district, + $options: "i", + }; + if (province) + query["affected_locations.address.province"] = { + $regex: province, + $options: "i", + }; + } + + const warnings = await Warning.find(query) + .populate("created_by", "name workId associated_department") + .populate("resolved_by", "name workId associated_department") + .populate("updates.updated_by", "name workId associated_department") + .populate( + "response_actions.performed_by", + "name workId associated_department", + ) + .sort({ created_at: -1 }) + .limit(parseInt(limit)) + .skip((page - 1) * limit); + + const total = await Warning.countDocuments(query); + + res.status(200).json({ + warnings, + currentPage: parseInt(page), + totalPages: Math.ceil(total / parseInt(limit)), + totalWarnings: total, + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +// Get warning by ID +export const getWarningById = async (req, res) => { + try { + const warning = await Warning.findById(req.params.id) + .populate("created_by", "name workId associated_department") + .populate("resolved_by", "name workId associated_department") + .populate("updates.updated_by", "name workId associated_department") + .populate( + "response_actions.performed_by", + "name workId associated_department", + ); + + if (!warning) { + return res.status(404).json({ error: "Warning not found" }); + } + + res.status(200).json(warning); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +// Get active warnings for public feed +export const getActiveWarnings = async (req, res) => { + try { + const warnings = await Warning.find({ + status: { $in: ["active", "monitoring"] }, + }) + .select( + "title disaster_category severity affected_locations status updates response_actions created_at", + ) + .sort({ created_at: -1 }) + .lean(); + + res.status(200).json({ + success: true, + data: warnings, + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; diff --git a/middlewares/authMiddleware.js b/middlewares/authMiddleware.js index 294ae4c..f2e9315 100644 --- a/middlewares/authMiddleware.js +++ b/middlewares/authMiddleware.js @@ -50,35 +50,9 @@ export const protectRoute = async (req, res, next) => { } }; -// Middleware for checking user types -export const verifyUserType = (allowedTypes) => { - return (req, res, next) => { - try { - // Check if user exists and has the correct type - if (!req.user || !allowedTypes.includes(req.user.type)) { - // Changed from userType to type - return res.status(403).json({ - success: false, - message: "Access denied. Insufficient permissions.", - }); - } - next(); - } catch (error) { - console.error("User Type Verification Error:", error); - return res.status(500).json({ - success: false, - message: "Error verifying user type.", - }); - } - }; -}; - -// Middleware for verified users export const verifyVerifiedUser = async (req, res, next) => { try { - // Check if user exists and is verified - if (!req.user || !req.user.verification_status) { - // Changed from isVerified to verification_status + if (!req.user || !req.user.isVerified) { return res.status(403).json({ success: false, message: "Access denied. This action requires a verified user.", @@ -86,7 +60,6 @@ export const verifyVerifiedUser = async (req, res, next) => { } next(); } catch (error) { - console.error("Verified User Check Error:", error); return res.status(500).json({ success: false, message: "Error checking user verification status.", @@ -94,26 +67,6 @@ export const verifyVerifiedUser = async (req, res, next) => { } }; -// Middleware for admin users -export const verifyAdmin = async (req, res, next) => { - try { - // Check if user exists and is admin - if (!req.user || req.user.type !== "admin") { - // Changed from userType to type - return res.status(403).json({ - success: false, - message: "Access denied. Admin privileges required.", - }); - } - next(); - } catch (error) { - console.error("Admin Verification Error:", error); - return res.status(500).json({ - success: false, - message: "Error verifying admin status.", - }); - } -}; export const verifyToken = (req, res, next) => { console.log("Cookies:", req.cookies); @@ -129,6 +82,7 @@ export const verifyToken = (req, res, next) => { try { const verified = jwt.verify(token, process.env.JWT_SECRET); req.user = verified; + next(); } catch (error) { res.status(401).json({ success: false, message: "Invalid Token." }); diff --git a/models/alerts.js b/models/alerts.js index afd2656..724ba8e 100644 --- a/models/alerts.js +++ b/models/alerts.js @@ -36,7 +36,7 @@ const alertSchema = new Schema( ); // Automatically adds createdAt and updatedAt // Optional: Geospatial index if you need to query by location -alertSchema.index({ location: "2dsphere" }); // Only if you're storing location info +// alertSchema.index({ location: "2dsphere" }); // Only if you're storing location info // Optional: Transform function to clean up output (remove _id and __v) alertSchema.set("toJSON", { diff --git a/models/incidentReport.js b/models/incidentReport.js deleted file mode 100644 index 4801168..0000000 --- a/models/incidentReport.js +++ /dev/null @@ -1,73 +0,0 @@ -import mongoose, { Schema } from "mongoose"; - -const locationSchema = new Schema({ - latitude: { type: Number, required: true, min: -90, max: 90 }, - longitude: { type: Number, required: true, min: -180, max: 180 }, - address: { - city: { type: String, required: true }, - district: { type: String, required: true }, - province: { type: String, required: true }, - details: String, - }, -}); - -// Main schema for incident reports -const incidentReportSchema = new Schema( - { - title: { type: String, required: true, trim: true }, - disaster_category: { - type: String, - required: true, - enum: ["flood", "fire", "earthquake", "landslide", "cyclone"], // Define disaster types - }, - description: { type: String, required: true, trim: true }, - location: { type: locationSchema, required: true }, // Use subschema for location - date_time: { type: Date, required: true }, - user_id: { type: Schema.Types.ObjectId, ref: "User", required: true }, // Reference to User - verified_by: { - type: [Schema.Types.ObjectId], - ref: "User", - default: [], // Array of user references - }, - images: { - type: [String], // Array of URLs - validate: { - validator: function (v) { - return v.every( - (url) => typeof url === "string" && url.startsWith("http"), - ); - }, - message: "All images must be valid URLs", - }, - required: true, - }, - severity: { - type: String, - required: true, - enum: ["low", "medium", "high"], - }, - response_status: { - type: String, - required: true, - enum: ["pending", "dispatched", "resolved"], - }, - }, - { timestamps: true }, -); // Automatically adds `createdAt` and `updatedAt` - -// Indexes -incidentReportSchema.index({ disaster_category: 1 }); -incidentReportSchema.index({ location: "2dsphere" }); // Geospatial index for location - -// Transform output to clean up API responses -incidentReportSchema.set("toJSON", { - transform: (doc, ret) => { - ret.id = ret._id.toString(); - delete ret._id; - delete ret.__v; - }, -}); - -// Model export -const IncidentReports = mongoose.model("IncidentReports", incidentReportSchema); -export default IncidentReports; diff --git a/models/resources.js b/models/resources.js index a957bf2..ebaee33 100644 --- a/models/resources.js +++ b/models/resources.js @@ -94,24 +94,141 @@ const resourceSchema = new Schema( added_by: { type: Schema.Types.ObjectId, ref: "User", - required: true, + // required: true, + }, + metadata: { + type: Schema.Types.Mixed, + validate: { + validator: function(v) { + switch(this.category) { + case 'facility': + return v?.capacity !== undefined; + case 'guide': + return v?.lastUpdated !== undefined; + case 'emergency_contact': + return v?.serviceHours !== undefined; + default: + return true; + } + }, + message: 'Invalid metadata for resource category' + } + }, + status: { + type: String, + enum: ['active', 'inactive', 'maintenance'], + default: 'active' + }, + tags: [{ + type: String, + trim: true + }], + operating_hours: { + type: Map, + of: { + open: String, + close: String, + is24Hours: Boolean + } + }, + capacity: { + type: Number, + min: 0, + required: function() { + return this.category === 'facility' && this.type === 'shelter'; + } }, + emergency_level: { + type: String, + enum: ['low', 'medium', 'high'], + required: function() { + return this.category === 'emergency_contact'; + } + }, + last_verified: { + type: Date, + default: Date.now + } }, { timestamps: true, - }, + } ); -resourceSchema.index({ "location.coordinates": "2dsphere" }); +// resourceSchema.index({ "location.coordinates": "2dsphere" }); resourceSchema.index({ category: 1, type: 1 }); +resourceSchema.index({ tags: 1 }); +resourceSchema.index({ status: 1 }); + +resourceSchema.methods.getAvailability = function() { + if (this.category === 'facility') { + return this.availability_status; + } + return null; +}; + +resourceSchema.methods.isOperational = function() { + return this.status === 'active' && + (this.category !== 'facility' || this.availability_status === 'open'); +}; + +resourceSchema.statics.findNearby = async function(coordinates, maxDistance = 5000) { + return this.find({ + 'location.coordinates': { + $near: { + $geometry: { + type: 'Point', + coordinates: coordinates + }, + $maxDistance: maxDistance + } + } + }); +}; + +resourceSchema.statics.findByType = async function(type) { + return this.find({ type, status: 'active' }); +}; + +resourceSchema.statics.findActiveGuides = async function() { + return this.find({ + category: 'guide', + status: 'active' + }).sort({ last_verified: -1 }); +}; + +resourceSchema.virtual('isVerified').get(function() { + const oneMonthAgo = new Date(); + oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1); + return this.last_verified >= oneMonthAgo; +}); + +resourceSchema.pre('save', function(next) { + if (this.isModified()) { + this.last_verified = new Date(); + } + next(); +}); resourceSchema.set("toJSON", { + virtuals: true, transform: (doc, ret) => { ret.id = ret._id.toString(); delete ret._id; delete ret.__v; + // Format dates + if (ret.last_verified) { + ret.last_verified = ret.last_verified.toISOString(); + } + if (ret.createdAt) { + ret.createdAt = ret.createdAt.toISOString(); + } + if (ret.updatedAt) { + ret.updatedAt = ret.updatedAt.toISOString(); + } }, }); const Resource = mongoose.model("Resource", resourceSchema); -export default Resource; + +export default Resource; \ No newline at end of file diff --git a/models/subscriptions.js b/models/subscriptions.js deleted file mode 100644 index 00e0efe..0000000 --- a/models/subscriptions.js +++ /dev/null @@ -1,62 +0,0 @@ -import mongoose, { Schema } from "mongoose"; - -const DISASTER_TYPES = ["flood", "earthquake", "fire", "landslide", "cyclone"]; -const REGIONS = ["Colombo", "Kandy", "Galle", "Jaffna", "Matara"]; -const FREQUENCIES = ["instant", "daily", "weekly"]; - -const subscriptionSchema = new Schema( - { - user_id: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - disaster_types: { - type: [String], - required: true, - validate: { - validator: (types) => - types.every((type) => DISASTER_TYPES.includes(type)), - message: "Invalid disaster type", - }, - }, - regions: { - type: [String], - required: true, - validate: { - validator: (regions) => - regions.every((region) => REGIONS.includes(region)), - message: "Invalid region", - }, - }, - notification_frequency: { - type: String, - required: true, - enum: FREQUENCIES, - default: "instant", - }, - is_active: { - type: Boolean, - default: true, - }, - }, - { - timestamps: true, - }, -); - -// Indexes for better query performance -subscriptionSchema.index({ user_id: 1 }); -subscriptionSchema.index({ disaster_types: 1 }); -subscriptionSchema.index({ regions: 1 }); - -subscriptionSchema.set("toJSON", { - transform: (doc, ret) => { - ret.id = ret._id.toString(); - delete ret._id; - delete ret.__v; - }, -}); - -const Subscription = mongoose.model("Subscription", subscriptionSchema); -export default Subscription; diff --git a/models/userReports.js b/models/userReports.js index 69e6c5e..1d8955c 100644 --- a/models/userReports.js +++ b/models/userReports.js @@ -1,17 +1,32 @@ import mongoose, { Schema } from "mongoose"; -// Updated location schema to include general location const locationSchema = new Schema({ - latitude: { type: Number, required: false }, // Made optional - longitude: { type: Number, required: false }, // Made optional + latitude: { type: Number, required: false }, + longitude: { type: Number, required: false }, address: { city: { type: String, required: true }, district: { type: String, required: true }, province: { type: String, required: true }, - details: { type: String }, // Optional additional details + details: { type: String }, }, }); +const verificationSchema = new Schema({ + verified_by: { type: Schema.Types.ObjectId, ref: "User" }, + verified_at: { type: Date }, + workId: { type: String }, + associated_department: { type: String }, + verification_time: { type: Number }, + severity: { + type: String, + enum: ["low", "medium", "high", "critical"], + required: function () { + return this.verification_status === "verified"; + }, + }, + notes: { type: String }, +}); + const userReportSchema = new Schema( { title: { type: String, required: true }, @@ -23,7 +38,6 @@ const userReportSchema = new Schema( description: { type: String, required: true }, location: { type: locationSchema, required: true }, date_time: { type: Date, default: Date.now }, - user_id: { type: Schema.Types.ObjectId, ref: "User", required: true }, images: { type: [String], validate: { @@ -35,27 +49,47 @@ const userReportSchema = new Schema( }, message: "All images must be valid URLs", }, - required: false, // Made optional + required: false, }, - status: { + reporter: { + type: Schema.Types.ObjectId, + ref: "User", + required: false, + }, + reporter_type: { + type: String, + enum: ["anonymous", "registered"], + default: "anonymous", + }, + verification_status: { type: String, - required: true, enum: ["pending", "verified", "dismissed"], - default: "pending", + default: function () { + return this.reporter ? "verified" : "pending"; + }, + }, + verification: { + type: verificationSchema, + required: function () { + return this.verification_status === "verified"; + }, }, }, { timestamps: true }, ); -// Indexes for better query performance +userReportSchema.index({ reporter: 1 }); +userReportSchema.index({ reporter_type: 1 }); +userReportSchema.index({ verification_status: 1 }); +userReportSchema.index({ "verification.severity": 1 }); +userReportSchema.index({ "verification.verified_by": 1 }); + userReportSchema.index({ "location.address.city": 1 }); userReportSchema.index({ "location.address.district": 1 }); userReportSchema.index({ "location.address.province": 1 }); userReportSchema.index({ disaster_category: 1 }); -userReportSchema.index({ status: 1 }); userReportSchema.index({ date_time: -1 }); -// Transform output userReportSchema.set("toJSON", { transform: (doc, ret) => { ret.id = ret._id.toString(); @@ -64,5 +98,18 @@ userReportSchema.set("toJSON", { }, }); +userReportSchema.pre("save", function (next) { + if (this.isNew && this.reporter) { + this.reporter_type = "registered"; + this.verification_status = "verified"; + this.verification = { + verified_by: this.reporter, + verified_at: new Date(), + severity: "medium", + }; + } + next(); +}); + const UserReports = mongoose.model("UserReports", userReportSchema); export default UserReports; diff --git a/models/users.js b/models/users.js index 5250a1a..bab458c 100644 --- a/models/users.js +++ b/models/users.js @@ -88,7 +88,7 @@ userSchema.index( { "location.latitude": 1, "location.longitude": 1 }, { sparse: true }, ); -userSchema.index({ workId: 1 }, { unique: true }); +// userSchema.index({ workId: 1 }, { unique: true }); const User = mongoose.model("User", userSchema); export default User; diff --git a/models/warning.js b/models/warning.js new file mode 100644 index 0000000..f6058e0 --- /dev/null +++ b/models/warning.js @@ -0,0 +1,153 @@ +import mongoose, { Schema } from "mongoose"; + +const locationSchema = new Schema({ + latitude: { type: Number, required: false }, + longitude: { type: Number, required: false }, + address: { + city: { type: String, required: true }, + district: { type: String, required: true }, + province: { type: String, required: true }, + details: { type: String }, + }, +}); + + +// Schema for individual updates to a warning +const warningUpdateSchema = new Schema({ + update_text: { type: String, required: true }, + severity_change: { + type: String, + enum: ["low", "medium", "high", "critical"], + required: false + }, + updated_at: { type: Date, default: Date.now } +}); + +// Schema for actions taken in response to the warning +const responseActionSchema = new Schema({ + action_type: { + type: String, + enum: ["evacuation", "rescue", "relief", "containment", "other"], + required: true + }, + description: { type: String, required: true }, + performed_at: { type: Date, default: Date.now }, + status: { + type: String, + enum: ["planned", "in_progress", "completed"], + default: "planned" + } +}); + +const warningSchema = new Schema({ + // Basic warning information + title: { type: String, required: true }, + description: { type: String, required: true }, + disaster_category: { + type: String, + required: true, + enum: ["flood", "fire", "earthquake", "landslide", "cyclone"] + }, + + // Warning specific fields + severity: { + type: String, + enum: ["low", "medium", "high", "critical"], + required: true + }, + expected_duration: { + start_time: { type: Date, required: true, default: Date.now }, + end_time: { type: Date } + }, + + // Location information (reusing your existing schema) + affected_locations: { + type: [locationSchema], + required: true, + validate: { + validator: function(v) { + return v.length > 0; + }, + message: "At least one affected location must be specified" + } + }, + + // Warning status + status: { + type: String, + enum: ["active", "monitoring", "resolved"], + default: "active" + }, + + // Related information + related_reports: [{ + type: Schema.Types.ObjectId, + ref: "UserReports" + }], + + // Updates and actions + updates: [warningUpdateSchema], + response_actions: [responseActionSchema], + + // Creator and resolution information + created_by: { + type: Schema.Types.ObjectId, + ref: "User", + required: true + }, + created_at: { type: Date, default: Date.now }, + resolved_by: { + type: Schema.Types.ObjectId, + ref: "User" + }, + resolved_at: { type: Date }, + resolution_notes: { type: String }, + + // Media + images: { + type: [String], + validate: { + validator: function(v) { + return !v.length || v.every(url => typeof url === "string" && url.startsWith("http")); + }, + message: "All images must be valid URLs" + } + } +}, { timestamps: true }); + +// Indexes for efficient querying +warningSchema.index({ status: 1 }); +warningSchema.index({ created_by: 1 }); +warningSchema.index({ severity: 1 }); +warningSchema.index({ disaster_category: 1 }); +warningSchema.index({ "affected_locations.address.city": 1 }); +warningSchema.index({ "affected_locations.address.district": 1 }); +warningSchema.index({ "affected_locations.address.province": 1 }); +warningSchema.index({ created_at: -1 }); + +// Transform for JSON output +warningSchema.set("toJSON", { + transform: (doc, ret) => { + ret.id = ret._id.toString(); + delete ret._id; + delete ret.__v; + } +}); + +// Pre-save middleware +warningSchema.pre("save", function(next) { + // If the warning is being marked as resolved + if (this.isModified("status") && this.status === "resolved") { + this.resolved_at = new Date(); + + // Ensure resolution notes are provided + if (!this.resolution_notes) { + const err = new Error("Resolution notes are required when marking a warning as resolved"); + return next(err); + } + } + next(); +}); + +const Warning = mongoose.model("Warning", warningSchema); +export default Warning; \ No newline at end of file diff --git a/routes/adminLogsRoutes.js b/routes/adminLogsRoutes.js index 8922ec7..06e8bae 100644 --- a/routes/adminLogsRoutes.js +++ b/routes/adminLogsRoutes.js @@ -5,13 +5,13 @@ import { } from "../controllers/adminLogsController.js"; import { protectRoute, - verifyUserType, + verifyVerifiedUser, verifyToken, } from "../middlewares/authMiddleware.js"; const router = express.Router(); -router.use(protectRoute, verifyToken, verifyUserType(["admin", "verified"])); +router.use(protectRoute, verifyToken, verifyVerifiedUser); router.get("/", getAdminLogs); router.get("/:id", getAdminLogById); diff --git a/routes/alertsRoutes.js b/routes/alertsRoutes.js index ca291c0..acf906d 100644 --- a/routes/alertsRoutes.js +++ b/routes/alertsRoutes.js @@ -8,7 +8,7 @@ import { } from "../controllers/alertsController.js"; import { protectRoute, - verifyUserType, + verifyVerifiedUser, verifyToken, } from "../middlewares/authMiddleware.js"; @@ -16,10 +16,10 @@ const router = express.Router(); router.use(protectRoute, verifyToken); -router.post("/", verifyUserType(["admin"]), createAlert); -router.get("/", verifyUserType(["admin"]), getAlerts); -router.get("/:id", verifyUserType(["admin"]), getAlertById); -router.put("/:id", verifyUserType(["admin"]), updateAlert); -router.delete("/:id", verifyUserType(["admin"]), deleteAlert); +router.post("/", verifyVerifiedUser, createAlert); +router.get("/", verifyVerifiedUser, getAlerts); +router.get("/:id", verifyVerifiedUser, getAlertById); +router.put("/:id",verifyVerifiedUser, updateAlert); +router.delete("/:id", verifyVerifiedUser, deleteAlert); export default router; diff --git a/routes/feedbackRoutes.js b/routes/feedbackRoutes.js index 01e63b9..a8a12bb 100644 --- a/routes/feedbackRoutes.js +++ b/routes/feedbackRoutes.js @@ -9,7 +9,7 @@ import { } from "../controllers/feedbackController.js"; import { protectRoute, - verifyUserType, + verifyVerifiedUser, verifyToken, } from "../middlewares/authMiddleware.js"; @@ -20,9 +20,9 @@ router.use(protectRoute, verifyToken); router.post("/", createFeedback); router.get("/my-feedback", getMyFeedback); -router.delete("/:id", verifyUserType(["admin"]), deleteFeedback); -router.get("/:id", verifyUserType(["admin"]), getFeedbackById); -router.get("/", verifyUserType(["admin"]), getFeedbacks); -router.put("/:id", verifyUserType(["admin"]), updateFeedback); +router.delete("/:id", verifyVerifiedUser, deleteFeedback); +router.get("/:id", verifyVerifiedUser, getFeedbackById); +router.get("/", verifyVerifiedUser, getFeedbacks); +router.put("/:id", verifyVerifiedUser, updateFeedback); export default router; diff --git a/routes/incidentReportsRoutes.js b/routes/incidentReportsRoutes.js deleted file mode 100644 index b99ac75..0000000 --- a/routes/incidentReportsRoutes.js +++ /dev/null @@ -1,32 +0,0 @@ -import express from "express"; -import { - createIncidentReport, - getIncidentReports, - getNearbyIncidents, - getIncidentReportById, - updateIncidentReport, - deleteIncidentReport, -} from "../controllers/incidentReportsController.js"; -import { - protectRoute, - verifyUserType, - verifyToken, -} from "../middlewares/authMiddleware.js"; - -const router = express.Router(); - -router.use(protectRoute, verifyToken); - -router.post("/", verifyUserType(["admin", "verified"]), createIncidentReport); -router.put("/:id", verifyUserType(["admin", "verified"]), updateIncidentReport); -router.delete( - "/:id", - verifyUserType(["admin", "verified"]), - deleteIncidentReport, -); - -router.get("/", getIncidentReports); -router.get("/nearby", getNearbyIncidents); -router.get("/:id", getIncidentReportById); - -export default router; diff --git a/routes/index.js b/routes/index.js index 449bf43..f528f10 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,26 +3,27 @@ import adminLogsRoutes from "./adminLogsRoutes.js"; import userRoutes from "./userRoutes.js"; import resourceRoutes from "./resourceRoutes.js"; import userReportRoutes from "./userReportRoutes.js"; -import incidentReportsRoutes from "./incidentReportsRoutes.js"; import feedbackRoutes from "./feedbackRoutes.js"; import weatherRoutes from "./weatherRoutes.js"; import alertsRoute from "./alertsRoutes.js"; import notificationRoutes from "./notificationRoutes.js"; -import subscriptionRoutes from "./subscriptionRoutes.js"; import locationRoutes from "./locationRoutes.js"; +import warningRoutes from "./warningRoutes.js"; const router = express.Router(); router.use("/adminlogs", adminLogsRoutes); router.use("/user", userRoutes); -router.use("/resource", resourceRoutes); +router.use("/resources", resourceRoutes); router.use("/userReport", userReportRoutes); -router.use("/incidentReport", incidentReportsRoutes); + router.use("/feedback", feedbackRoutes); router.use("/weather", weatherRoutes); -router.use("/alerts", alertsRoute); -router.use("/notifications", notificationRoutes); -router.use("/subscriptions", subscriptionRoutes); -router.use("/location", locationRoutes); + +// router.use("/alerts", alertsRoute); +// router.use("/notifications", notificationRoutes); +// router.use("/location", locationRoutes); + +router.use("/warning", warningRoutes); export default router; diff --git a/routes/locationRoutes.js b/routes/locationRoutes.js index b93a4ab..4d45d42 100644 --- a/routes/locationRoutes.js +++ b/routes/locationRoutes.js @@ -8,7 +8,7 @@ import { } from "../controllers/locationController.js"; import { protectRoute, - verifyUserType, + verifyVerifiedUser, verifyToken, } from "../middlewares/authMiddleware.js"; diff --git a/routes/resourceRoutes.js b/routes/resourceRoutes.js index 7100c57..336cf1d 100644 --- a/routes/resourceRoutes.js +++ b/routes/resourceRoutes.js @@ -11,15 +11,14 @@ import { } from "../controllers/resourceController.js"; import { protectRoute, - verifyUserType, + verifyVerifiedUser, verifyToken, } from "../middlewares/authMiddleware.js"; +import Resource from "../models/resources.js"; const router = express.Router(); -router.use(protectRoute, verifyToken); - -// Public routes +// Public routes (no authentication required) router.get("/facilities", getFacilities); router.get("/guides", getGuides); router.get("/emergency-contacts", getEmergencyContacts); @@ -27,8 +26,39 @@ router.get("/facilities/nearby", getNearbyFacilities); router.get("/:id", getResourceById); // Protected routes -router.post("/", verifyUserType(["admin", "verified"]), createResource); -router.put("/:id", verifyUserType(["admin", "verified"]), updateResource); -router.delete("/:id", verifyUserType(["admin", "verified"]), deleteResource); +router.use(protectRoute, verifyToken); + +router.post("/", verifyVerifiedUser, createResource); +router.put("/:id", verifyVerifiedUser, updateResource); +router.delete("/:id", verifyVerifiedUser, deleteResource); + +// Additional routes for advanced features +router.get("/verified/last-month", verifyVerifiedUser, async (req, res) => { + const oneMonthAgo = new Date(); + oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1); + + const resources = await Resource.find({ + last_verified: { $gte: oneMonthAgo } + }).populate("added_by", "name email"); + + res.json({ success: true, data: resources }); +}); -export default router; +router.get("/", verifyVerifiedUser, async (req, res) => { + try { + const resources = await Resource.find() + .populate("added_by", "name email") + .sort({ updatedAt: -1 }); + + res.json({ + success: true, + resources + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); +export default router; \ No newline at end of file diff --git a/routes/subscriptionRoutes.js b/routes/subscriptionRoutes.js deleted file mode 100644 index b0042e3..0000000 --- a/routes/subscriptionRoutes.js +++ /dev/null @@ -1,19 +0,0 @@ -import express from "express"; -import { - createSubscription, - getMySubscription, - updateMySubscription, - deleteMySubscription, -} from "../controllers/subscriptionController.js"; -import { protectRoute, verifyToken } from "../middlewares/authMiddleware.js"; - -const router = express.Router(); - -router.use(protectRoute, verifyToken); - -router.post("/", createSubscription); -router.get("/my-subscription", getMySubscription); -router.put("/my-subscription", updateMySubscription); -router.delete("/my-subscription", deleteMySubscription); - -export default router; diff --git a/routes/userReportRoutes.js b/routes/userReportRoutes.js index ba8c8ce..f5d7c04 100644 --- a/routes/userReportRoutes.js +++ b/routes/userReportRoutes.js @@ -1,29 +1,45 @@ import express from "express"; import { createUserReport, + verifyReport, + dismissReport, getUserReports, - getUserReportById, - updateUserReport, - deleteUserReport, + getReportsByUser, + getReportStats, + getVerificationStats, + getReportAnalytics, + getPublicFeed, + getFeedReports, + getFeedStats, + getFeedUpdates, } from "../controllers/userReportController.js"; import { protectRoute, - verifyUserType, + verifyVerifiedUser, verifyToken, } from "../middlewares/authMiddleware.js"; const router = express.Router(); +router.get("/feed", getPublicFeed); +router.post("/", createUserReport); +router.get("/public", getUserReports); +router.get('/reports', getFeedReports); +router.get('/feedstats', getFeedStats); +router.get('/updates', getFeedUpdates); + router.use(protectRoute, verifyToken); -router.post("/", verifyUserType(["user", "verified"]), createUserReport); -router.get("/", getUserReports); -router.get("/:id", getUserReportById); -router.put("/:id", verifyUserType(["user", "verified"]), updateUserReport); -router.delete( - "/:id", - verifyUserType(["user", "admin", "verified"]), - deleteUserReport, -); +router.get("/my-reports", getReportsByUser); +router.get("/stats/verification", getVerificationStats); +router.get("/stats/analytics", getReportAnalytics); + +router.use(verifyVerifiedUser); + +router.post("/:id/verify", verifyReport); +router.post("/:id/dismiss", dismissReport); + +router.get("/stats", getReportStats); +router.get("/verified", getUserReports); export default router; diff --git a/routes/userRoutes.js b/routes/userRoutes.js index b33ee9e..85d0318 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -22,6 +22,6 @@ router.get("/", getAllUsers); router.get("/:id", getUserById); router.put("/:id", updateUser); router.delete("/:id", deleteUser); -router.post("/:id/change-password", changePassword); +router.post("/:id/changepassword", changePassword); -export default router; \ No newline at end of file +export default router; diff --git a/routes/warningRoutes.js b/routes/warningRoutes.js new file mode 100644 index 0000000..239c748 --- /dev/null +++ b/routes/warningRoutes.js @@ -0,0 +1,36 @@ +import express from "express"; +import { + createWarning, + addWarningUpdate, + addResponseAction, + updateActionStatus, + resolveWarning, + getWarnings, + getWarningById, + getActiveWarnings +} from "../controllers/warningController.js"; +import { + protectRoute, + verifyVerifiedUser, + verifyToken +} from "../middlewares/authMiddleware.js"; + +const router = express.Router(); + +// Public routes +router.get("/active", getActiveWarnings); +router.get("/", getWarnings); +router.get("/:id", getWarningById); + +// Protected routes - require authentication +router.use(protectRoute, verifyToken); + +// Routes for verified users only +router.use(verifyVerifiedUser); +router.post("/", createWarning); +router.post("/:id/updates", addWarningUpdate); +router.post("/:id/actions", addResponseAction); +router.patch("/:id/actions/:actionId", updateActionStatus); +router.post("/:id/resolve", resolveWarning); + +export default router; \ No newline at end of file diff --git a/routes/weatherRoutes.js b/routes/weatherRoutes.js index 3a1b921..584cfb9 100644 --- a/routes/weatherRoutes.js +++ b/routes/weatherRoutes.js @@ -6,13 +6,13 @@ import { } from "../controllers/weatherController.js"; import { protectRoute, - verifyUserType, + verifyVerifiedUser, verifyToken, } from "../middlewares/authMiddleware.js"; const router = express.Router(); -router.use(protectRoute, verifyToken, verifyUserType(["admin"])); +router.use(protectRoute, verifyToken, verifyVerifiedUser); router.get("/current", getCurrentLocationWeather); router.get("/saved/:locationId", getSavedLocationWeather); diff --git a/utils/index.js b/utils/index.js index b93f804..93d5482 100644 --- a/utils/index.js +++ b/utils/index.js @@ -7,8 +7,8 @@ dotenv.config(); export const connectMongoose = async () => { try { const connection = await mongoose.connect(process.env.MONGODB_URI, { - useNewUrlParser: true, - useUnifiedTopology: true, + // useNewUrlParser: true, + // useUnifiedTopology: true, serverApi: ServerApiVersion.v1, });