├── .circleci └── config.yml ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── README.md ├── ai ├── communityEngagementAnalysis.js ├── fraudDetection.js ├── recommendationEngine.js ├── sentimentAnalysis.js ├── trainingData │ ├── communityData.json │ ├── feedbackData.json │ ├── transactionData.json │ └── userBehaviorData.json └── userBehaviorAnalysis.js ├── assets ├── branding │ ├── brand_guide.pdf │ └── color_palette.png ├── designs │ ├── mockups │ │ └── mockups.png │ └── wireframes.png ├── icons │ └── ai_icon.png └── logos │ ├── pi_connect_logo.svg │ └── pi_logo.svg ├── blockchain ├── blockchainService.js ├── oracles │ ├── eventOracle.js │ └── priceOracle.js └── smartContracts │ ├── aiContract.sol │ ├── analyticsContract.py │ ├── charityContract.sol │ ├── communityContract.sol │ └── paymentContract.sol ├── docs ├── api_reference.md ├── architecture_overview.md ├── code_structure.md ├── contribution_guide.md └── setup.md ├── examples ├── ai_use_cases.md ├── analytics_use_cases.md ├── charity_donation_example.md ├── community_engagement_examples.md ├── e-commerce_integration.md ├── gaming_integration.md └── social_media_tipping.md ├── src ├── backend │ ├── app.js │ ├── config.js │ ├── constant.py │ ├── controllers │ │ ├── aiController.js │ │ ├── analyticsController.js │ │ ├── charityController.js │ │ ├── communityController.js │ │ ├── eventController.js │ │ ├── fraudDetectionController.js │ │ ├── notificationController.js │ │ ├── paymentController.js │ │ └── userController.js │ ├── middleware │ │ ├── authMiddleware.js │ │ ├── errorMiddleware.js │ │ ├── inputValidationMiddleware.js │ │ ├── loggingMiddleware.js │ │ └── rateLimitMiddleware.js │ ├── models │ │ ├── aiModel.js │ │ ├── analyticsModel.js │ │ ├── charityModel.js │ │ ├── communityModel.js │ │ ├── fraudModel.js │ │ ├── notificationModel.js │ │ ├── transactionModel.js │ │ └── userModel.js │ ├── routes │ │ ├── aiRoutes.js │ │ ├── analyticsRoutes.js │ │ ├── charityRoutes.js │ │ ├── communityRoutes.js │ │ ├── eventRoutes.js │ │ ├── fraudRoutes.js │ │ ├── notificationRoutes.js │ │ ├── paymentRoutes.js │ │ └── userRoutes.js │ ├── server.js │ ├── services │ │ ├── aiService.js │ │ ├── analyticsService.js │ │ ├── charityService.js │ │ ├── communityService.js │ │ ├── eventService.js │ │ ├── fraudService.js │ │ ├── notificationService.js │ │ ├── paymentService.js │ │ └── userService.js │ └── utils │ │ ├── emailService.js │ │ ├── encryptionService.js │ │ ├── logger.js │ │ ├── notificationUtils.js │ │ ├── smsService.js │ │ └── validationService.js ├── frontend │ ├── assets │ │ ├── PiConnet_logo.png │ │ └── icons │ │ │ └── icons.png │ ├── components │ │ ├── AiRecommendations.js │ │ ├── AnalyticsDashboard.js │ │ ├── CommunityFeed.js │ │ ├── EventCard.js │ │ ├── Footer.js │ │ ├── FraudAlert.js │ │ ├── Header.js │ │ ├── NotificationBell.js │ │ └── TransactionList.js │ ├── css │ │ ├── AiRecommendations.css │ │ ├── CommunityFeed.css │ │ ├── EventCard.css │ │ ├── FraudAlert.css │ │ ├── animations.css │ │ ├── responsive.css │ │ ├── styles.css │ │ └── theme.css │ ├── index.html │ ├── js │ │ ├── ai.js │ │ ├── analytics.js │ │ ├── app.js │ │ ├── charity.js │ │ ├── community.js │ │ ├── event.js │ │ ├── fraud.js │ │ ├── notification.js │ │ ├── payment.js │ │ └── user.js │ └── scripts │ │ ├── build.js │ │ └── deploy.js └── scripts │ ├── build.js │ ├── deploy.js │ └── test.js └── tests ├── integration ├── aiRoutes.test.js ├── analyticsRoutes.test.js ├── charityRoutes.test.js ├── communityRoutes.test.js ├── eventRoutes.test.js ├── fraudRoutes.test.js ├── notificationRoutes.test.js ├── paymentRoutes.test.js └── userRoutes.test.js ├── setupTests.js └── unit ├── aiController.test.js ├── analyticsController.test.js ├── charityController.test.js ├── communityController.test.js ├── eventController.test.js ├── fraudController.test.js ├── notificationController.test.js ├── paymentController.test.js └── userController.test.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Use the latest 2.1 version of CircleCI pipeline process engine. 2 | # See: https://circleci.com/docs/configuration-reference 3 | version: 2.1 4 | 5 | # Define a job to be invoked later in a workflow. 6 | # See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs 7 | jobs: 8 | say-hello: 9 | # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. 10 | # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job 11 | docker: 12 | # Specify the version you desire here 13 | # See: https://circleci.com/developer/images/image/cimg/base 14 | - image: cimg/base:current 15 | 16 | # Add steps to the job 17 | # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps 18 | steps: 19 | # Checkout the code as the first step. 20 | - checkout 21 | - run: 22 | name: "Say hello" 23 | command: "echo Hello, World!" 24 | 25 | # Orchestrate jobs using workflows 26 | # See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows 27 | workflows: 28 | say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. 29 | # Inside the workflow, you define the jobs you want to run. 30 | jobs: 31 | - say-hello -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js dependencies 2 | node_modules/ 3 | package-lock.json 4 | 5 | # Build output 6 | dist/ 7 | build/ 8 | *.log 9 | 10 | # Environment variables 11 | .env 12 | .env.local 13 | .env.*.local 14 | 15 | # IDE and editor files 16 | .vscode/ 17 | .idea/ 18 | *.sublime-project 19 | *.sublime-workspace 20 | 21 | # Operating system files 22 | .DS_Store 23 | Thumbs.db 24 | 25 | # Temporary files 26 | *.tmp 27 | *.temp 28 | *.bak 29 | 30 | # Coverage reports 31 | coverage/ 32 | *.lcov 33 | 34 | # Machine learning model files (if applicable) 35 | models/ 36 | *.h5 37 | *.pkl 38 | 39 | # Redis data files (if applicable) 40 | *.rdb 41 | *.aof 42 | 43 | # Miscellaneous 44 | *.tgz 45 | *.zip 46 | *.tar.gz 47 | -------------------------------------------------------------------------------- /ai/communityEngagementAnalysis.js: -------------------------------------------------------------------------------- 1 | // communityEngagementAnalysis.js 2 | 3 | // Sample data structure for community members and events 4 | const members = [ 5 | { id: 1, name: 'Alice', contributions: 5, isActive: true }, 6 | { id: 2, name: 'Bob', contributions: 3, isActive: true }, 7 | { id: 3, name: 'Charlie', contributions: 0, isActive: false }, 8 | ]; 9 | 10 | const events = [ 11 | { id: 1, title: 'Community Meetup', participants: [1, 2], date: '2023-10-01' }, 12 | { id: 2, title: 'Online Workshop', participants: [1], date: '2023-10-15' }, 13 | { id: 3, title: 'Charity Event', participants: [], date: '2023-10-20' }, 14 | ]; 15 | 16 | /** 17 | * Calculate the total number of active members in the community. 18 | * @returns {number} The count of active members. 19 | */ 20 | function getActiveMemberCount() { 21 | return members.filter(member => member.isActive).length; 22 | } 23 | 24 | /** 25 | * Calculate the total contributions made by all members. 26 | * @returns {number} The total contributions. 27 | */ 28 | function getTotalContributions() { 29 | return members.reduce((total, member) => total + member.contributions, 0); 30 | } 31 | 32 | /** 33 | * Calculate the participation rate for events. 34 | * @returns {Object} An object containing event participation rates. 35 | */ 36 | function getEventParticipationRates() { 37 | const participationRates = events.map(event => { 38 | const totalParticipants = event.participants.length; 39 | const participationRate = totalParticipants / members.length; // Assuming all members can participate 40 | return { 41 | eventId: event.id, 42 | title: event.title, 43 | participationRate: participationRate, 44 | }; 45 | }); 46 | return participationRates; 47 | } 48 | 49 | /** 50 | * Get a summary of community engagement metrics. 51 | * @returns {Object} An object containing various engagement metrics. 52 | */ 53 | function getEngagementSummary() { 54 | const activeMemberCount = getActiveMemberCount(); 55 | const totalContributions = getTotalContributions(); 56 | const eventParticipationRates = getEventParticipationRates(); 57 | 58 | return { 59 | activeMemberCount, 60 | totalContributions, 61 | eventParticipationRates, 62 | }; 63 | } 64 | 65 | // Example usage 66 | const engagementSummary = getEngagementSummary(); 67 | console.log('Community Engagement Summary:', engagementSummary); 68 | 69 | module.exports = { 70 | getActiveMemberCount, 71 | getTotalContributions, 72 | getEventParticipationRates, 73 | getEngagementSummary, 74 | }; 75 | -------------------------------------------------------------------------------- /ai/fraudDetection.js: -------------------------------------------------------------------------------- 1 | const { createClient } = require('redis'); 2 | const { RandomForestClassifier } = require('ml-random-forest'); 3 | const { Matrix } = require('ml-matrix'); 4 | const axios = require('axios'); 5 | 6 | class FraudDetection { 7 | constructor(redisUrl, modelPath) { 8 | this.redisClient = createClient({ url: redisUrl }); 9 | this.model = null; 10 | this.modelPath = modelPath; 11 | this.init(); 12 | } 13 | 14 | // Initialize the Redis client and load the model 15 | async init() { 16 | await this.redisClient.connect(); 17 | console.log('Connected to Redis'); 18 | this.model = await this.loadModel(); 19 | } 20 | 21 | // Load the pre-trained model 22 | async loadModel() { 23 | // Load the model from the specified path (this is a placeholder) 24 | // In practice, you would use a library to load your model 25 | return new RandomForestClassifier(); // Placeholder for the actual model loading 26 | } 27 | 28 | // Function to fetch transaction data from the blockchain 29 | async fetchTransactionData() { 30 | try { 31 | const response = await axios.get('https://api.example.com/transactions'); // Replace with actual API 32 | return response.data.transactions; // Assuming the API returns an array of transactions 33 | } catch (error) { 34 | console.error('Error fetching transaction data:', error.message); 35 | return []; 36 | } 37 | } 38 | 39 | // Function to preprocess the transaction data 40 | preprocessData(transactions) { 41 | return transactions.map(transaction => { 42 | return [ 43 | transaction.amount, 44 | transaction.timestamp, 45 | transaction.sender, 46 | transaction.receiver, 47 | // Add more features as needed 48 | ]; 49 | }); 50 | } 51 | 52 | // Function to predict fraud for new transactions 53 | async predictFraud(transactions) { 54 | const processedData = this.preprocessData(transactions); 55 | const predictions = this.model.predict(new Matrix(processedData)); 56 | return predictions; 57 | } 58 | 59 | // Function to log predictions to Redis 60 | async logPredictions(transactions, predictions) { 61 | for (let i = 0; i < transactions.length; i++) { 62 | await this.redisClient.hSet(`transaction:${transactions[i].id}`, { 63 | isFraud: predictions[i], 64 | ...transactions[i], 65 | }); 66 | console.log(`Logged prediction for transaction ${transactions[i].id}`); 67 | } 68 | } 69 | 70 | // Function to close the Redis connection 71 | async close() { 72 | await this.redisClient.quit(); 73 | console.log('Disconnected from Redis'); 74 | } 75 | } 76 | 77 | // Example usage 78 | const redisUrl = 'redis://localhost:6379'; 79 | const fraudDetection = new FraudDetection(redisUrl, './models/fraudDetectionModel'); 80 | 81 | // Fetch transactions and predict fraud 82 | fraudDetection.fetchTransactionData() 83 | .then(transactions => { 84 | return fraudDetection.predictFraud(transactions); 85 | }) 86 | .then(predictions => { 87 | console.log('Fraud Predictions:', predictions); 88 | return fraudDetection.logPredictions(transactions, predictions); 89 | }) 90 | .catch(error => { 91 | console.error('Error in fraud detection process:', error); 92 | }); 93 | 94 | // Export the FraudDetection class 95 | module.exports = FraudDetection; 96 | -------------------------------------------------------------------------------- /ai/recommendationEngine.js: -------------------------------------------------------------------------------- 1 | const { createClient } = require('redis'); 2 | const axios = require('axios'); 3 | 4 | class RecommendationEngine { 5 | constructor(redisUrl, apiUrl) { 6 | this.redisClient = createClient({ url: redisUrl }); 7 | this.apiUrl = apiUrl; 8 | this.init(); 9 | } 10 | 11 | // Initialize the Redis client 12 | async init() { 13 | await this.redisClient.connect(); 14 | console.log('Connected to Redis'); 15 | } 16 | 17 | // Function to get user preferences from Redis 18 | async getUser Preferences(userId) { 19 | const preferences = await this.redisClient.hGetAll(`user:${userId}:preferences`); 20 | return preferences; 21 | } 22 | 23 | // Function to fetch items from the API 24 | async fetchItems() { 25 | try { 26 | const response = await axios.get(this.apiUrl); 27 | return response.data.items; // Assuming the API returns an array of items 28 | } catch (error) { 29 | console.error('Error fetching items:', error.message); 30 | return []; 31 | } 32 | } 33 | 34 | // Function to generate recommendations based on user preferences 35 | async generateRecommendations(userId) { 36 | const preferences = await this.getUser Preferences(userId); 37 | const items = await this.fetchItems(); 38 | 39 | // Simple recommendation logic based on preferences 40 | const recommendations = items.filter(item => { 41 | return preferences.categories.includes(item.category); 42 | }); 43 | 44 | return recommendations; 45 | } 46 | 47 | // Function to save user preferences 48 | async saveUser Preferences(userId, preferences) { 49 | await this.redisClient.hSet(`user:${userId}:preferences`, preferences); 50 | console.log(`Preferences saved for user ${userId}`); 51 | } 52 | 53 | // Function to close the Redis connection 54 | async close() { 55 | await this.redisClient.quit(); 56 | console.log('Disconnected from Redis'); 57 | } 58 | } 59 | 60 | // Example usage 61 | const redisUrl = 'redis://localhost:6379'; 62 | const apiUrl = 'https://api.example.com/items'; 63 | const recommendationEngine = new RecommendationEngine(redisUrl, apiUrl); 64 | 65 | // Generate recommendations for a user 66 | const userId = 'user123'; 67 | recommendationEngine.generateRecommendations(userId) 68 | .then(recommendations => { 69 | console.log('Recommendations:', recommendations); 70 | }) 71 | .catch(error => { 72 | console.error('Error generating recommendations:', error); 73 | }); 74 | 75 | // Export the RecommendationEngine class 76 | module.exports = RecommendationEngine; 77 | -------------------------------------------------------------------------------- /ai/sentimentAnalysis.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const { SentimentAnalyzer } = require('natural'); 3 | const { PorterStemmer } = require('natural'); 4 | 5 | class SentimentAnalysis { 6 | constructor(apiUrl) { 7 | this.apiUrl = apiUrl; 8 | this.analyzer = new SentimentAnalyzer('English', PorterStemmer, 'afinn'); 9 | } 10 | 11 | // Function to fetch user feedback data 12 | async fetchUser Feedback() { 13 | try { 14 | const response = await axios.get(this.apiUrl); 15 | return response.data.feedback; // Assuming the API returns an array of feedback 16 | } catch (error) { 17 | console.error('Error fetching user feedback:', error.message); 18 | return []; 19 | } 20 | } 21 | 22 | // Function to analyze sentiment of feedback 23 | analyzeSentiment(feedback) { 24 | return feedback.map(item => { 25 | const score = this.analyzer.getSentiment(item.text.split(' ')); 26 | let sentiment = 'neutral'; 27 | if (score > 0) { 28 | sentiment = 'positive'; 29 | } else if (score < 0) { 30 | sentiment = 'negative'; 31 | } 32 | return { 33 | text: item.text, 34 | score: score, 35 | sentiment: sentiment, 36 | }; 37 | }); 38 | } 39 | 40 | // Function to generate sentiment report 41 | generateReport(sentimentResults) { 42 | const report = { 43 | totalFeedback: sentimentResults.length, 44 | positive: sentimentResults.filter(result => result.sentiment === 'positive').length, 45 | negative: sentimentResults.filter(result => result.sentiment === 'negative').length, 46 | neutral: sentimentResults.filter(result => result.sentiment === 'neutral').length, 47 | }; 48 | console.log('Sentiment Report:', report); 49 | return report; 50 | } 51 | 52 | // Main function to execute sentiment analysis 53 | async run() { 54 | const feedback = await this.fetchUser Feedback(); 55 | const sentimentResults = this.analyzeSentiment(feedback); 56 | this.generateReport(sentimentResults); 57 | } 58 | } 59 | 60 | // Example usage 61 | const apiUrl = 'https://api.example.com/user-feedback'; // Replace with actual API 62 | const sentimentAnalysis = new SentimentAnalysis(apiUrl); 63 | sentimentAnalysis.run() 64 | .catch(error => { 65 | console.error('Error in sentiment analysis process:', error); 66 | }); 67 | 68 | // Export the SentimentAnalysis class 69 | module.exports = SentimentAnalysis; 70 | -------------------------------------------------------------------------------- /ai/trainingData/communityData.json: -------------------------------------------------------------------------------- 1 | { 2 | "members": [ 3 | { 4 | "memberId": "member1", 5 | "name": "Alice", 6 | "joinDate": "2023-01-15", 7 | "isActive": true, 8 | "contributions": 10, 9 | "eventsParticipated": ["event1", "event2"] 10 | }, 11 | { 12 | "memberId": "member2", 13 | "name": "Bob", 14 | "joinDate": "2023-02-20", 15 | "isActive": true, 16 | "contributions": 5, 17 | "eventsParticipated": ["event1"] 18 | }, 19 | { 20 | "memberId": "member3", 21 | "name": "Charlie", 22 | "joinDate": "2023-03-10", 23 | "isActive": false, 24 | "contributions": 0, 25 | "eventsParticipated": [] 26 | }, 27 | { 28 | "memberId": "member4", 29 | "name": "David", 30 | "joinDate": "2023-04-05", 31 | "isActive": true, 32 | "contributions": 8, 33 | "eventsParticipated": ["event2", "event3"] 34 | } 35 | ], 36 | "events": [ 37 | { 38 | "eventId": "event1", 39 | "title": "Community Meetup", 40 | "description": "A gathering for community members to discuss ideas and projects.", 41 | "date": "2023-10-01", 42 | "participants": ["member1", "member2"] 43 | }, 44 | { 45 | "eventId": "event2", 46 | "title": "Online Workshop", 47 | "description": "An interactive workshop on community building.", 48 | "date": "2023-10-15", 49 | "participants": ["member1", "member4"] 50 | }, 51 | { 52 | "eventId": "event3", 53 | "title": "Charity Event", 54 | "description": "A charity event to raise funds for local causes.", 55 | "date": "2023-10-20", 56 | "participants": ["member4"] 57 | } 58 | ], 59 | "interactions": [ 60 | { 61 | "interactionId": "interaction1", 62 | "memberId": "member1", 63 | "eventId": "event1", 64 | "action": "comment", 65 | "content": "Excited for this meetup!", 66 | "timestamp": "2023-09-25T10:00:00Z" 67 | }, 68 | { 69 | "interactionId": "interaction2", 70 | "memberId": "member2", 71 | "eventId": "event1", 72 | "action": "like", 73 | "content": null, 74 | "timestamp": "2023-09-26T11:15:00Z" 75 | }, 76 | { 77 | "interactionId": "interaction3", 78 | "memberId": "member4", 79 | "eventId": "event2", 80 | "action": "register", 81 | "content": null, 82 | "timestamp": "2023-09-27T09:30:00Z" 83 | }, 84 | { 85 | "interactionId": "interaction4", 86 | "memberId": "member1", 87 | "eventId": "event3", 88 | "action": "share", 89 | "content": "Looking forward to the charity event!", 90 | "timestamp": "2023-09-28T14:00:00Z" 91 | } 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /ai/trainingData/feedbackData.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "userId": "user1", 4 | "feedback": "I absolutely love this product! It has changed my life for the better.", 5 | "rating": 5, 6 | "timestamp": "2023-10-01T10:00:00Z" 7 | }, 8 | { 9 | "userId": "user2", 10 | "feedback": "The service was okay, but I expected more features.", 11 | "rating": 3, 12 | "timestamp": "2023-10-02T11:15:00Z" 13 | }, 14 | { 15 | "userId": "user3", 16 | "feedback": "I had a terrible experience. The product did not work as advertised.", 17 | "rating": 1, 18 | "timestamp": "2023-10-03T12:30:00Z" 19 | }, 20 | { 21 | "userId": "user4", 22 | "feedback": "Great value for the price! Highly recommend it to others.", 23 | "rating": 4, 24 | "timestamp": "2023-10-04T09:45:00Z" 25 | }, 26 | { 27 | "userId": "user5", 28 | "feedback": "Not satisfied with the customer support. They were unhelpful.", 29 | "rating": 2, 30 | "timestamp": "2023-10-05T14:20:00Z" 31 | }, 32 | { 33 | "userId": "user6", 34 | "feedback": "This is the best purchase I've made this year! Fantastic quality.", 35 | "rating": 5, 36 | "timestamp": "2023-10-06T08:00:00Z" 37 | }, 38 | { 39 | "userId": "user7", 40 | "feedback": "I like the product, but it could use some improvements.", 41 | "rating": 3, 42 | "timestamp": "2023-10-07T15:30:00Z" 43 | }, 44 | { 45 | "userId": "user8", 46 | "feedback": "Absolutely terrible! I want a refund.", 47 | "rating": 1, 48 | "timestamp": "2023-10-08T10:10:00Z" 49 | }, 50 | { 51 | "userId": "user9", 52 | "feedback": "Decent product, but the delivery was late.", 53 | "rating": 3, 54 | "timestamp": "2023-10-09T13:25:00Z" 55 | }, 56 | { 57 | "userId": "user10", 58 | "feedback": "I am very happy with my purchase. Will buy again!", 59 | "rating": 5, 60 | "timestamp": "2023-10-10T16:00:00Z" 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /ai/trainingData/transactionData.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "transactionId": "tx001", 4 | "userId": "user123", 5 | "charityId": "charity456", 6 | "amount": 50.0, 7 | "timestamp": "2023-10-01T12:00:00Z", 8 | "paymentMethod": "credit_card", 9 | "status": "completed", 10 | "location": "New York, USA" 11 | }, 12 | { 13 | "transactionId": "tx002", 14 | "userId": "user124", 15 | "charityId": "charity789", 16 | "amount": 25.0, 17 | "timestamp": "2023-10-01T12:05:00Z", 18 | "paymentMethod": "paypal", 19 | "status": "completed", 20 | "location": "Los Angeles, USA" 21 | }, 22 | { 23 | "transactionId": "tx003", 24 | "userId": "user125", 25 | "charityId": "charity456", 26 | "amount": 100.0, 27 | "timestamp": "2023-10-01T12:10:00Z", 28 | "paymentMethod": "bank_transfer", 29 | "status": "pending", 30 | "location": "Chicago, USA" 31 | }, 32 | { 33 | "transactionId": "tx004", 34 | "userId": "user123", 35 | "charityId": "charity456", 36 | "amount": 75.0, 37 | "timestamp": "2023-10-01T12:15:00Z", 38 | "paymentMethod": "credit_card", 39 | "status": "completed", 40 | "location": "New York, USA" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /ai/trainingData/userBehaviorData.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "userId": "user123", 4 | "timestamp": "2023-10-01T12:00:00Z", 5 | "action": "donate", 6 | "amount": 50.0, 7 | "charityId": "charity456", 8 | "location": "New York, USA", 9 | "device": "mobile" 10 | }, 11 | { 12 | "userId": "user124", 13 | "timestamp": "2023-10-01T12:05:00Z", 14 | "action": "view", 15 | "charityId": "charity789", 16 | "location": "Los Angeles, USA", 17 | "device": "desktop" 18 | }, 19 | { 20 | "userId": "user125", 21 | "timestamp": "2023-10-01T12:10:00Z", 22 | "action": "share", 23 | "charityId": "charity456", 24 | "location": "Chicago, USA", 25 | "device": "tablet" 26 | }, 27 | { 28 | "userId": "user123", 29 | "timestamp": "2023-10-01T12:15:00Z", 30 | "action": "comment", 31 | "charityId": "charity456", 32 | "comment": "Great cause!", 33 | "location": "New York, USA", 34 | "device": "mobile" 35 | } 36 | ] 37 | -------------------------------------------------------------------------------- /ai/userBehaviorAnalysis.js: -------------------------------------------------------------------------------- 1 | // userBehaviorAnalysis.js 2 | 3 | // Sample data structure for user interactions 4 | const userInteractions = [ 5 | { userId: 'user1', actions: ['login', 'view', 'comment', 'like'], timestamp: '2023-10-01T10:00:00Z' }, 6 | { userId: 'user2', actions: ['login', 'view', 'like'], timestamp: '2023-10-01T11:00:00Z' }, 7 | { userId: 'user1', actions: ['login', 'view', 'share'], timestamp: '2023-10-02T09:00:00Z' }, 8 | { userId: 'user3', actions: ['login', 'comment'], timestamp: '2023-10-02T10:30:00Z' }, 9 | { userId: 'user2', actions: ['login', 'view', 'comment'], timestamp: '2023-10-03T12:00:00Z' }, 10 | ]; 11 | 12 | /** 13 | * Calculate the total number of unique users. 14 | * @returns {number} The count of unique users. 15 | */ 16 | function getUniqueUser Count() { 17 | const uniqueUsers = new Set(userInteractions.map(interaction => interaction.userId)); 18 | return uniqueUsers.size; 19 | } 20 | 21 | /** 22 | * Calculate the total number of actions performed by users. 23 | * @returns {number} The total number of actions. 24 | */ 25 | function getTotalActions() { 26 | return userInteractions.reduce((total, interaction) => total + interaction.actions.length, 0); 27 | } 28 | 29 | /** 30 | * Get the most common actions performed by users. 31 | * @returns {Object} An object containing action counts. 32 | */ 33 | function getMostCommonActions() { 34 | const actionCounts = {}; 35 | 36 | userInteractions.forEach(interaction => { 37 | interaction.actions.forEach(action => { 38 | actionCounts[action] = (actionCounts[action] || 0) + 1; 39 | }); 40 | }); 41 | 42 | return actionCounts; 43 | } 44 | 45 | /** 46 | * Get user engagement metrics over a specified time period. 47 | * @param {string} startDate - The start date in ISO format. 48 | * @param {string} endDate - The end date in ISO format. 49 | * @returns {Object} An object containing engagement metrics. 50 | */ 51 | function getEngagementMetrics(startDate, endDate) { 52 | const filteredInteractions = userInteractions.filter(interaction => { 53 | const timestamp = new Date(interaction.timestamp); 54 | return timestamp >= new Date(startDate) && timestamp <= new Date(endDate); 55 | }); 56 | 57 | const uniqueUsers = new Set(filteredInteractions.map(interaction => interaction.userId)); 58 | const totalActions = filteredInteractions.reduce((total, interaction) => total + interaction.actions.length, 0); 59 | 60 | return { 61 | uniqueUser Count: uniqueUsers.size, 62 | totalActions, 63 | }; 64 | } 65 | 66 | // Example usage 67 | const uniqueUser Count = getUniqueUser Count(); 68 | const totalActions = getTotalActions(); 69 | const mostCommonActions = getMostCommonActions(); 70 | const engagementMetrics = getEngagementMetrics('2023-10-01', '2023-10-02'); 71 | 72 | console.log('Unique User Count:', uniqueUser Count); 73 | console.log('Total Actions:', totalActions); 74 | console.log('Most Common Actions:', mostCommonActions); 75 | console.log('Engagement Metrics:', engagementMetrics); 76 | 77 | module.exports = { 78 | getUniqueUser Count, 79 | getTotalActions, 80 | getMostCommonActions, 81 | getEngagementMetrics, 82 | }; 83 | -------------------------------------------------------------------------------- /assets/branding/brand_guide.pdf: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/branding/color_palette.png: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/designs/mockups/mockups.png: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/designs/wireframes.png: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icons/ai_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KOSASIH/PiConnect-Ecosystem/66a132fa9f8714f3e421cafeec560a557782abc5/assets/icons/ai_icon.png -------------------------------------------------------------------------------- /assets/logos/pi_connect_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/logos/pi_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /blockchain/blockchainService.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3'); 2 | const { Transaction } = require('ethereumjs-tx').Transaction; 3 | const Common = require('ethereumjs-common').default; 4 | const dotenv = require('dotenv'); 5 | 6 | dotenv.config(); // Load environment variables from .env file 7 | 8 | // Initialize Web3 9 | const web3 = new Web3(new Web3.providers.HttpProvider(process.env.BLOCKCHAIN_NODE_URL)); 10 | 11 | // Smart contract ABI and address 12 | const contractABI = [ /* ABI array */ ]; 13 | const contractAddress = process.env.CONTRACT_ADDRESS; 14 | 15 | // Create contract instance 16 | const contract = new web3.eth.Contract(contractABI, contractAddress); 17 | 18 | // Function to get the balance of an address 19 | async function getBalance(address) { 20 | try { 21 | const balance = await contract.methods.balanceOf(address).call(); 22 | return web3.utils.fromWei(balance, 'ether'); // Convert from Wei to Ether 23 | } catch (error) { 24 | console.error('Error fetching balance:', error.message); 25 | throw new Error('Failed to fetch balance. Please try again later.'); 26 | } 27 | } 28 | 29 | // Function to send a transaction 30 | async function sendTransaction(fromAddress, toAddress, amount, privateKey) { 31 | try { 32 | const gasPrice = await web3.eth.getGasPrice(); // Get current gas price 33 | const nonce = await web3.eth.getTransactionCount(fromAddress); // Get nonce 34 | 35 | const tx = { 36 | nonce: web3.utils.toHex(nonce), 37 | gasLimit: web3.utils.toHex(21000), // Set gas limit 38 | gasPrice: web3.utils.toHex(gasPrice), 39 | to: toAddress, 40 | value: web3.utils.toHex(web3.utils.toWei(amount.toString(), 'ether')), 41 | }; 42 | 43 | const common = Common.forCustomChain( 44 | 'mainnet', 45 | { 46 | name: 'custom', 47 | networkId: 1, 48 | chainId: 1, 49 | }, 50 | 'petersburg' 51 | ); 52 | 53 | const transaction = new Transaction(tx, { chain: 'mainnet', hardfork: 'petersburg' }); 54 | const privateKeyBuffer = Buffer.from(privateKey, 'hex'); 55 | transaction.sign(privateKeyBuffer); 56 | 57 | const serializedTx = transaction.serialize(); 58 | const receipt = await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')); 59 | return receipt; 60 | } catch (error) { 61 | console.error('Error sending transaction:', error.message); 62 | throw new Error('Transaction failed. Please check your details and try again.'); 63 | } 64 | } 65 | 66 | // Function to listen for events from the smart contract 67 | function listenForEvents() { 68 | contract.events.Transfer({ 69 | filter: { from: '0xYourAddress' }, // Filter for specific address 70 | fromBlock: 'latest' 71 | }) 72 | .on('data', event => { 73 | console.log('Transfer event detected:', event); 74 | }) 75 | .on('error', error => { 76 | console.error('Error listening for events:', error.message); 77 | }); 78 | } 79 | 80 | // Function to monitor transaction status 81 | async function monitorTransaction(txHash) { 82 | try { 83 | const receipt = await web3.eth.getTransactionReceipt(txHash); 84 | if (receipt) { 85 | return receipt; 86 | } else { 87 | throw new Error('Transaction not found. It may still be pending.'); 88 | } 89 | } catch (error) { 90 | console.error('Error monitoring transaction:', error.message); 91 | throw new Error('Failed to monitor transaction. Please try again later.'); 92 | } 93 | } 94 | 95 | // Export functions 96 | module.exports = { 97 | getBalance, 98 | sendTransaction, 99 | listenForEvents, 100 | monitorTransaction, 101 | }; 102 | -------------------------------------------------------------------------------- /blockchain/oracles/eventOracle.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const { EventEmitter } = require('events'); 3 | 4 | class EventOracle extends EventEmitter { 5 | constructor(apiUrl) { 6 | super(); 7 | this.apiUrl = apiUrl; 8 | this.events = []; 9 | this.updateInterval = 30000; // Update every 30 seconds 10 | this.startEventFeed(); 11 | } 12 | 13 | // Function to fetch the latest events from the API 14 | async fetchEvents() { 15 | try { 16 | const response = await axios.get(this.apiUrl); 17 | const newEvents = this.extractEvents(response.data); 18 | this.processEvents(newEvents); 19 | } catch (error) { 20 | console.error('Error fetching events:', error.message); 21 | } 22 | } 23 | 24 | // Function to extract events from the API response 25 | extractEvents(data) { 26 | // Assuming the API returns a JSON object with an 'events' array 27 | // Modify this according to the actual API response structure 28 | return data.events || []; 29 | } 30 | 31 | // Function to process new events 32 | processEvents(newEvents) { 33 | newEvents.forEach(event => { 34 | if (!this.events.includes(event.id)) { // Check for duplicates 35 | this.events.push(event.id); 36 | this.emit('eventReceived', event); // Emit event when a new event is received 37 | } 38 | }); 39 | } 40 | 41 | // Function to start the event feed 42 | startEventFeed() { 43 | this.fetchEvents(); // Initial fetch 44 | setInterval(() => { 45 | this.fetchEvents(); 46 | }, this.updateInterval); 47 | } 48 | } 49 | 50 | // Example usage 51 | const eventOracle = new EventOracle('https://api.example.com/events'); 52 | 53 | eventOracle.on('eventReceived', (event) => { 54 | console.log(`New event received: ${JSON.stringify(event)}`); 55 | // Here you can add logic to interact with your blockchain smart contracts 56 | }); 57 | 58 | // Export the EventOracle class 59 | module.exports = EventOracle; 60 | -------------------------------------------------------------------------------- /blockchain/oracles/priceOracle.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const { EventEmitter } = require('events'); 3 | 4 | class PriceOracle extends EventEmitter { 5 | constructor(apiUrl, assetSymbol) { 6 | super(); 7 | this.apiUrl = apiUrl; 8 | this.assetSymbol = assetSymbol; 9 | this.currentPrice = 0; 10 | this.updateInterval = 60000; // Update every minute 11 | this.startPriceFeed(); 12 | } 13 | 14 | // Function to fetch the latest price from the API 15 | async fetchPrice() { 16 | try { 17 | const response = await axios.get(this.apiUrl); 18 | const price = this.extractPrice(response.data); 19 | this.currentPrice = price; 20 | this.emit('priceUpdated', price); // Emit event when price is updated 21 | } catch (error) { 22 | console.error('Error fetching price:', error.message); 23 | } 24 | } 25 | 26 | // Function to extract the price from the API response 27 | extractPrice(data) { 28 | // Assuming the API returns a JSON object with a 'price' field 29 | // Modify this according to the actual API response structure 30 | return data.price; 31 | } 32 | 33 | // Function to start the price feed 34 | startPriceFeed() { 35 | this.fetchPrice(); // Initial fetch 36 | setInterval(() => { 37 | this.fetchPrice(); 38 | }, this.updateInterval); 39 | } 40 | 41 | // Function to get the current price 42 | getCurrentPrice() { 43 | return this.currentPrice; 44 | } 45 | } 46 | 47 | // Example usage 48 | const priceOracle = new PriceOracle('https://api.example.com/price', 'ETH'); 49 | 50 | priceOracle.on('priceUpdated', (newPrice) => { 51 | console.log(`New price for ${priceOracle.assetSymbol}: $${newPrice}`); 52 | }); 53 | 54 | // Export the PriceOracle class 55 | module.exports = PriceOracle; 56 | -------------------------------------------------------------------------------- /blockchain/smartContracts/aiContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract AIContract { 5 | struct Model { 6 | string name; 7 | string description; 8 | address owner; 9 | string modelURI; // URI to the model's metadata or location 10 | bool isActive; 11 | } 12 | 13 | mapping(uint256 => Model) public models; // Mapping of model ID to Model 14 | mapping(address => uint256[]) public userModels; // Mapping of user address to their model IDs 15 | uint256 public modelCount; // Counter for the number of models 16 | 17 | event ModelCreated(uint256 indexed modelId, string name, address indexed owner); 18 | event ModelUpdated(uint256 indexed modelId, string name, bool isActive); 19 | event ModelDeleted(uint256 indexed modelId); 20 | 21 | modifier onlyOwner(uint256 modelId) { 22 | require(msg.sender == models[modelId].owner, "Not the model owner"); 23 | _; 24 | } 25 | 26 | // Function to create a new AI model 27 | function createModel(string memory name, string memory description, string memory modelURI) public { 28 | modelCount++; 29 | models[modelCount] = Model(name, description, msg.sender, modelURI, true); 30 | userModels[msg.sender].push(modelCount); 31 | emit ModelCreated(modelCount, name, msg.sender); 32 | } 33 | 34 | // Function to update an existing AI model 35 | function updateModel(uint256 modelId, string memory name, string memory description, bool isActive) public onlyOwner(modelId) { 36 | Model storage model = models[modelId]; 37 | model.name = name; 38 | model.description = description; 39 | model.isActive = isActive; 40 | emit ModelUpdated(modelId, name, isActive); 41 | } 42 | 43 | // Function to delete an AI model 44 | function deleteModel(uint256 modelId) public onlyOwner(modelId) { 45 | delete models[modelId]; 46 | emit ModelDeleted(modelId); 47 | } 48 | 49 | // Function to get model details 50 | function getModel(uint256 modelId) public view returns (string memory name, string memory description, address owner, string memory modelURI, bool isActive) { 51 | Model memory model = models[modelId]; 52 | return (model.name, model.description, model.owner, model.modelURI, model.isActive); 53 | } 54 | 55 | // Function to get models created by a user 56 | function getUser Models(address user) public view returns (uint256[] memory) { 57 | return userModels[user]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /blockchain/smartContracts/analyticsContract.py: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 6 | 7 | contract AnalyticsContract is Ownable { 8 | using SafeMath for uint256; 9 | 10 | // Struct to represent donation analytics 11 | struct DonationData { 12 | uint256 totalDonations; 13 | uint256 donationCount; 14 | } 15 | 16 | // Mapping to store analytics data for each charity 17 | mapping(string => DonationData) private charityAnalytics; 18 | 19 | // Mapping to store analytics data for each user 20 | mapping(address => DonationData) private userAnalytics; 21 | 22 | // Event to log donations for analytics 23 | event DonationLogged(address indexed donor, string indexed charityId, uint256 amount); 24 | 25 | // Function to log a donation 26 | function logDonation(address donor, string memory charityId, uint256 amount) external onlyOwner { 27 | require(amount > 0, "Donation must be greater than zero"); 28 | 29 | // Update charity analytics 30 | charityAnalytics[charityId].totalDonations = charityAnalytics[charityId].totalDonations.add(amount); 31 | charityAnalytics[charityId].donationCount = charityAnalytics[charityId].donationCount.add(1); 32 | 33 | // Update user analytics 34 | userAnalytics[donor].totalDonations = userAnalytics[donor].totalDonations.add(amount); 35 | userAnalytics[donor].donationCount = userAnalytics[donor].donationCount.add(1); 36 | 37 | emit DonationLogged(donor, charityId, amount); 38 | } 39 | 40 | // Function to get total donations for a charity 41 | function getCharityAnalytics(string memory charityId) public view returns (uint256 totalDonations, uint256 donationCount) { 42 | DonationData memory data = charityAnalytics[charityId]; 43 | return (data.totalDonations, data.donationCount); 44 | } 45 | 46 | // Function to get total donations for a user 47 | function getUser Analytics(address user) public view returns (uint256 totalDonations, uint256 donationCount) { 48 | DonationData memory data = userAnalytics[user]; 49 | return (data.totalDonations, data.donationCount); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /blockchain/smartContracts/charityContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 6 | 7 | contract CharityContract is Ownable { 8 | using SafeMath for uint256; 9 | 10 | // Struct to represent a charity 11 | struct Charity { 12 | string id; 13 | string name; 14 | address payable wallet; 15 | uint256 totalDonations; 16 | bool isActive; 17 | } 18 | 19 | // Mapping to store charities by ID 20 | mapping(string => Charity) public charities; 21 | 22 | // Event to log donations 23 | event DonationReceived(address indexed donor, string indexed charityId, uint256 amount); 24 | event CharityAdded(string indexed charityId, string name, address wallet); 25 | event CharityUpdated(string indexed charityId, string name, address wallet, bool isActive); 26 | 27 | // Function to add a new charity 28 | function addCharity(string memory charityId, string memory name, address payable wallet) public onlyOwner { 29 | require(wallet != address(0), "Invalid wallet address"); 30 | require(!charities[charityId].isActive, "Charity already exists"); 31 | 32 | charities[charityId] = Charity({ 33 | id: charityId, 34 | name: name, 35 | wallet: wallet, 36 | totalDonations: 0, 37 | isActive: true 38 | }); 39 | 40 | emit CharityAdded(charityId, name, wallet); 41 | } 42 | 43 | // Function to update charity details 44 | function updateCharity(string memory charityId, string memory name, address payable wallet, bool isActive) public onlyOwner { 45 | require(charities[charityId].isActive, "Charity does not exist"); 46 | 47 | charities[charityId].name = name; 48 | charities[charityId].wallet = wallet; 49 | charities[charityId].isActive = isActive; 50 | 51 | emit CharityUpdated(charityId, name, wallet, isActive); 52 | } 53 | 54 | // Function to donate to a charity 55 | function donate(string memory charityId) public payable { 56 | require(charities[charityId].isActive, "Charity is not active"); 57 | require(msg.value > 0, "Donation must be greater than zero"); 58 | 59 | Charity storage charity = charities[charityId]; 60 | charity.wallet.transfer(msg.value); // Transfer funds to the charity's wallet 61 | charity.totalDonations = charity.totalDonations.add(msg.value); // Update total donations 62 | 63 | emit DonationReceived(msg.sender, charityId, msg.value); 64 | } 65 | 66 | // Function to get total donations for a charity 67 | function getTotalDonations(string memory charityId) public view returns (uint256) { 68 | require(charities[charityId].isActive, "Charity is not active"); 69 | return charities[charityId].totalDonations; 70 | } 71 | 72 | // Function to get charity details 73 | function getCharity(string memory charityId) public view returns (string memory, address, uint256, bool) { 74 | require(charities[charityId].isActive, "Charity is not active"); 75 | Charity memory charity = charities[charityId]; 76 | return (charity.name, charity.wallet, charity.totalDonations, charity.isActive); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /blockchain/smartContracts/communityContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract CommunityContract { 5 | struct Member { 6 | address memberAddress; 7 | string name; 8 | uint256 contributions; 9 | bool isActive; 10 | } 11 | 12 | struct Event { 13 | string title; 14 | string description; 15 | uint256 date; 16 | address organizer; 17 | bool isActive; 18 | } 19 | 20 | mapping(address => Member) public members; // Mapping of member address to Member 21 | mapping(uint256 => Event) public events; // Mapping of event ID to Event 22 | uint256 public eventCount; // Counter for the number of events 23 | 24 | event MemberJoined(address indexed memberAddress, string name); 25 | event MemberUpdated(address indexed memberAddress, string name, bool isActive); 26 | event EventCreated(uint256 indexed eventId, string title, address indexed organizer); 27 | event EventUpdated(uint256 indexed eventId, string title, bool isActive); 28 | 29 | modifier onlyActiveMember() { 30 | require(members[msg.sender].isActive, "Not an active member"); 31 | _; 32 | } 33 | 34 | // Function to join the community 35 | function joinCommunity(string memory name) public { 36 | require(members[msg.sender].memberAddress == address(0), "Already a member"); 37 | members[msg.sender] = Member(msg.sender, name, 0, true); 38 | emit MemberJoined(msg.sender, name); 39 | } 40 | 41 | // Function to update member details 42 | function updateMember(string memory name, bool isActive) public onlyActiveMember { 43 | Member storage member = members[msg.sender]; 44 | member.name = name; 45 | member.isActive = isActive; 46 | emit MemberUpdated(msg.sender, name, isActive); 47 | } 48 | 49 | // Function to create a new event 50 | function createEvent(string memory title, string memory description, uint256 date) public onlyActiveMember { 51 | eventCount++; 52 | events[eventCount] = Event(title, description, date, msg.sender, true); 53 | emit EventCreated(eventCount, title, msg.sender); 54 | } 55 | 56 | // Function to update an existing event 57 | function updateEvent(uint256 eventId, string memory title, bool isActive) public { 58 | Event storage eventDetail = events[eventId]; 59 | require(msg.sender == eventDetail.organizer, "Not the event organizer"); 60 | eventDetail.title = title; 61 | eventDetail.isActive = isActive; 62 | emit EventUpdated(eventId, title, isActive); 63 | } 64 | 65 | // Function to get member details 66 | function getMember(address memberAddress) public view returns (string memory name, uint256 contributions, bool isActive) { 67 | Member memory member = members[memberAddress]; 68 | return (member.name, member.contributions, member.isActive); 69 | } 70 | 71 | // Function to get event details 72 | function getEvent(uint256 eventId) public view returns (string memory title, string memory description, uint256 date, address organizer, bool isActive) { 73 | Event memory eventDetail = events[eventId]; 74 | return (eventDetail.title, eventDetail.description, eventDetail.date, eventDetail.organizer, eventDetail.isActive); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /blockchain/smartContracts/paymentContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 6 | 7 | contract PaymentContract is Ownable { 8 | using SafeMath for uint256; 9 | 10 | // Event to log payments 11 | event PaymentReceived(address indexed donor, uint256 amount, string charityId); 12 | event Withdrawal(address indexed to, uint256 amount); 13 | 14 | // Mapping to track total donations per charity 15 | mapping(string => uint256) public charityDonations; 16 | 17 | // Function to receive payments 18 | receive() external payable { 19 | require(msg.value > 0, "Payment must be greater than zero"); 20 | emit PaymentReceived(msg.sender, msg.value, "default"); // Default charity ID 21 | } 22 | 23 | // Function to donate to a specific charity 24 | function donate(string memory charityId) public payable { 25 | require(msg.value > 0, "Donation must be greater than zero"); 26 | charityDonations[charityId] = charityDonations[charityId].add(msg.value); 27 | emit PaymentReceived(msg.sender, msg.value, charityId); 28 | } 29 | 30 | // Function to withdraw funds to a specified address 31 | function withdraw(address payable to, uint256 amount) public onlyOwner { 32 | require(amount <= address(this).balance, "Insufficient balance"); 33 | to.transfer(amount); 34 | emit Withdrawal(to, amount); 35 | } 36 | 37 | // Function to get the total donations for a specific charity 38 | function getTotalDonations(string memory charityId) public view returns (uint256) { 39 | return charityDonations[charityId]; 40 | } 41 | 42 | // Function to get the contract's balance 43 | function getContractBalance() public view returns (uint256) { 44 | return address(this).balance; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/setup.md: -------------------------------------------------------------------------------- 1 | # PiConnect Ecosystem - Setup Instructions 2 | 3 | Welcome to the PiConnect Ecosystem project! This document provides step-by-step instructions for setting up the development environment. 4 | 5 | ## Prerequisites 6 | 7 | Before you begin, ensure you have the following installed on your machine: 8 | 9 | - **Node.js** (version 14 or higher) 10 | - **npm** (Node Package Manager, comes with Node.js) 11 | - **Git** (for version control) 12 | 13 | ## Getting Started 14 | 15 | Follow these steps to set up the project: 16 | 17 | ### 1. Clone the Repository 18 | 19 | Open your terminal and run the following command to clone the repository: 20 | 21 | ```bash 22 | git clone https://github.com/KOSASIH/piconnect-ecosystem.git 23 | ``` 24 | 25 | Replace `yourusername` with your GitHub username. 26 | 27 | ### 2. Navigate to the Project Directory 28 | 29 | Change into the project directory: 30 | 31 | ```bash 32 | cd piconnect-ecosystem 33 | ``` 34 | 35 | ### 3. Install Dependencies 36 | 37 | Install the required dependencies for both the frontend and backend: 38 | 39 | ```bash 40 | # For the frontend 41 | cd src/frontend 42 | npm install 43 | 44 | # For the backend 45 | cd ../backend 46 | npm install 47 | ``` 48 | 49 | ### 4. Configure Environment Variables 50 | 51 | Create a `.env` file in the `src/backend` directory and add the necessary environment variables. Here’s an example: 52 | 53 | ```env 54 | PORT=5000 55 | MONGODB_URI=mongodb://localhost:27017/piconnect 56 | JWT_SECRET=your_jwt_secret 57 | ``` 58 | 59 | Make sure to replace the values with your actual configuration. 60 | 61 | ### 5. Start the Development Servers 62 | 63 | You can start both the frontend and backend servers. Open two terminal windows or tabs: 64 | 65 | **Terminal 1** (Backend): 66 | 67 | ```bash 68 | cd src/backend 69 | npm start 70 | ``` 71 | 72 | **Terminal 2** (Frontend): 73 | 74 | ```bash 75 | cd src/frontend 76 | npm start 77 | ``` 78 | 79 | ### 6. Access the Application 80 | 81 | Once both servers are running, you can access the application in your web browser at: 82 | 83 | ``` 84 | http://localhost:3000 85 | ``` 86 | 87 | ### 7. Running Tests 88 | 89 | To run tests for the frontend and backend, use the following commands: 90 | 91 | **Frontend Tests:** 92 | 93 | ```bash 94 | cd src/frontend 95 | npm test 96 | ``` 97 | 98 | **Backend Tests:** 99 | 100 | ```bash 101 | cd src/backend 102 | npm test 103 | ``` 104 | 105 | ### 8. Building for Production 106 | 107 | To create a production build of the frontend application, run the following command in the `src/frontend` directory: 108 | 109 | ```bash 110 | npm run build 111 | ``` 112 | 113 | This will generate a `dist` folder containing the optimized production files. 114 | 115 | ### 9. Deployment 116 | 117 | Refer to the `deploy.js` script in the `src/frontend/scripts` directory for deployment instructions. 118 | 119 | ## Troubleshooting 120 | 121 | If you encounter any issues during setup, please check the following: 122 | 123 | - Ensure that all dependencies are installed correctly. 124 | - Verify that your environment variables are set up properly. 125 | - Check the console for any error messages and address them accordingly. 126 | 127 | ## Conclusion 128 | 129 | You are now ready to start developing with the PiConnect Ecosystem! If you have any questions or need further assistance, feel free to reach out to the project maintainers. 130 | 131 | Happy coding! 132 | -------------------------------------------------------------------------------- /src/backend/app.js: -------------------------------------------------------------------------------- 1 | // backend/app.js 2 | const express = require('express'); 3 | const mongoose = require('mongoose'); 4 | const bodyParser = require('body-parser'); 5 | const cors = require('cors'); 6 | const morgan = require('morgan'); 7 | const helmet = require('helmet'); 8 | const rateLimit = require('express-rate-limit'); 9 | const { connectDB } = require('./config'); 10 | 11 | // Import routes 12 | const paymentRoutes = require('./routes/paymentRoutes'); 13 | const userRoutes = require('./routes/userRoutes'); 14 | const charityRoutes = require('./routes/charityRoutes'); 15 | const analyticsRoutes = require('./routes/analyticsRoutes'); 16 | const notificationRoutes = require('./routes/notificationRoutes'); 17 | 18 | const app = express(); 19 | 20 | // Middleware 21 | app.use(cors()); 22 | app.use(helmet()); 23 | app.use(morgan('combined')); 24 | app.use(bodyParser.json()); 25 | app.use(bodyParser.urlencoded({ extended: true })); 26 | 27 | // Rate limiting 28 | const limiter = rateLimit({ 29 | windowMs: 15 * 60 * 1000, // 15 minutes 30 | max: 100 // Limit each IP to 100 requests per windowMs 31 | }); 32 | app.use(limiter); 33 | 34 | // Database connection 35 | connectDB(); 36 | 37 | // Routes 38 | app.use('/api/payments', paymentRoutes); 39 | app.use('/api/users', userRoutes); 40 | app.use('/api/charities', charityRoutes); 41 | app.use('/api/analytics', analyticsRoutes); 42 | app.use('/api/notifications', notificationRoutes); 43 | 44 | // Error handling middleware 45 | app.use((err, req, res, next) => { 46 | console.error(err.stack); 47 | res.status(500).send({ message: 'Something went wrong!' }); 48 | }); 49 | 50 | // Start the server 51 | const PORT = process.env.PORT || 5000; 52 | app.listen(PORT, () => { 53 | console.log(`Server is running on port ${PORT}`); 54 | }); 55 | -------------------------------------------------------------------------------- /src/backend/config.js: -------------------------------------------------------------------------------- 1 | // src/backend/config.js 2 | const mongoose = require('mongoose'); 3 | const dotenv = require('dotenv'); 4 | 5 | // Load environment variables from .env file 6 | dotenv.config(); 7 | 8 | // MongoDB connection function 9 | const connectDB = async () => { 10 | try { 11 | const mongoURI = process.env.MONGODB_URI; // MongoDB URI from environment variables 12 | await mongoose.connect(mongoURI, { 13 | useNewUrlParser: true, 14 | useUnifiedTopology: true, 15 | useCreateIndex: true, // Ensure indexes are created 16 | useFindAndModify: false // Avoid deprecation warnings 17 | }); 18 | console.log('MongoDB connected successfully'); 19 | } catch (error) { 20 | console.error('MongoDB connection failed:', error); 21 | process.exit(1); // Exit the process with failure 22 | } 23 | }; 24 | 25 | // Export the connectDB function 26 | module.exports = { connectDB }; 27 | -------------------------------------------------------------------------------- /src/backend/controllers/aiController.js: -------------------------------------------------------------------------------- 1 | // aiController.js 2 | 3 | const UserService = require('../services/userService'); 4 | const RecommendationService = require('../services/aiService'); 5 | const SentimentAnalysisService = require('../services/sentimentAnalysisService'); 6 | const { validateUser Input } = require('../middleware/inputValidationMiddleware'); 7 | 8 | // AI Controller for handling AI-driven features and recommendations 9 | 10 | class AIController { 11 | // Get personalized recommendations for a user 12 | async getPersonalizedRecommendations(req, res) { 13 | try { 14 | const userId = req.user.id; // Assuming user ID is available in the request 15 | const userPreferences = await UserService.getUser Preferences(userId); 16 | const recommendations = await RecommendationService.generateRecommendations(userPreferences); 17 | return res.status(200).json({ success: true, recommendations }); 18 | } catch (error) { 19 | console.error('Error fetching recommendations:', error); 20 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 21 | } 22 | } 23 | 24 | // Analyze user feedback for sentiment 25 | async analyzeUser Feedback(req, res) { 26 | try { 27 | const { feedback } = req.body; 28 | validateUser Input(feedback); // Validate input 29 | const sentimentScore = await SentimentAnalysisService.analyzeSentiment(feedback); 30 | return res.status(200).json({ success: true, sentimentScore }); 31 | } catch (error) { 32 | console.error('Error analyzing feedback:', error); 33 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 34 | } 35 | } 36 | 37 | // Get user behavior analysis 38 | async getUser BehaviorAnalysis(req, res) { 39 | try { 40 | const userId = req.user.id; // Assuming user ID is available in the request 41 | const behaviorData = await UserService.getUser BehaviorData(userId); 42 | const analysisResults = await RecommendationService.analyzeUser Behavior(behaviorData); 43 | return res.status(200).json({ success: true, analysisResults }); 44 | } catch (error) { 45 | console.error('Error fetching user behavior analysis:', error); 46 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 47 | } 48 | } 49 | 50 | // Get community engagement analysis 51 | async getCommunityEngagementAnalysis(req, res) { 52 | try { 53 | const communityId = req.params.communityId; // Get community ID from request parameters 54 | const engagementData = await UserService.getCommunityEngagementData(communityId); 55 | const analysisResults = await RecommendationService.analyzeCommunityEngagement(engagementData); 56 | return res.status(200).json({ success: true, analysisResults }); 57 | } catch (error) { 58 | console.error('Error fetching community engagement analysis:', error); 59 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 60 | } 61 | } 62 | } 63 | 64 | module.exports = new AIController(); 65 | -------------------------------------------------------------------------------- /src/backend/controllers/analyticsController.js: -------------------------------------------------------------------------------- 1 | // src/backend/controllers/analyticsController.js 2 | const Payment = require('../models/transactionModel'); 3 | const User = require('../models/userModel'); 4 | const Charity = require('../models/charityModel'); 5 | 6 | // Get overall analytics data 7 | exports.getAnalyticsData = async (req, res) => { 8 | try { 9 | // Count total users 10 | const totalUsers = await User.countDocuments(); 11 | 12 | // Count total payments 13 | const totalPayments = await Payment.countDocuments(); 14 | 15 | // Calculate total amount of payments 16 | const totalPaymentAmount = await Payment.aggregate([ 17 | { $group: { _id: null, totalAmount: { $sum: '$amount' } } } 18 | ]); 19 | 20 | // Count total charities 21 | const totalCharities = await Charity.countDocuments(); 22 | 23 | // Prepare analytics data 24 | const analyticsData = { 25 | totalUsers, 26 | totalPayments, 27 | totalPaymentAmount: totalPaymentAmount[0] ? totalPaymentAmount[0].totalAmount : 0, 28 | totalCharities 29 | }; 30 | 31 | // Respond with analytics data 32 | res.status(200).send(analyticsData); 33 | } catch (error) { 34 | console.error('Error retrieving analytics data:', error); 35 | res.status(500).send({ message: 'Failed to retrieve analytics data', error }); 36 | } 37 | }; 38 | 39 | // Get payment analytics for a specific user 40 | exports.getUser PaymentAnalytics = async (req, res) => { 41 | const { userId } = req.params; 42 | 43 | try { 44 | // Retrieve payment history for the user 45 | const payments = await Payment.find({ userId }); 46 | 47 | // Calculate total payments and total amount 48 | const totalPayments = payments.length; 49 | const totalPaymentAmount = payments.reduce((acc, payment) => acc + payment.amount, 0); 50 | 51 | // Prepare user payment analytics data 52 | const userPaymentAnalytics = { 53 | userId, 54 | totalPayments, 55 | totalPaymentAmount 56 | }; 57 | 58 | // Respond with user payment analytics data 59 | res.status(200).send(userPaymentAnalytics); 60 | } catch (error) { 61 | console.error('Error retrieving user payment analytics:', error); 62 | res.status(500).send({ message: 'Failed to retrieve user payment analytics', error }); 63 | } 64 | }; 65 | 66 | // Get charity analytics 67 | exports.getCharityAnalytics = async (req, res) => { 68 | try { 69 | // Count total charities 70 | const totalCharities = await Charity.countDocuments(); 71 | 72 | // Prepare charity analytics data 73 | const charityAnalytics = { 74 | totalCharities 75 | }; 76 | 77 | // Respond with charity analytics data 78 | res.status(200).send(charityAnalytics); 79 | } catch (error) { 80 | console.error('Error retrieving charity analytics:', error); 81 | res.status(500).send({ message: 'Failed to retrieve charity analytics', error }); 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /src/backend/controllers/charityController.js: -------------------------------------------------------------------------------- 1 | // src/backend/controllers/charityController.js 2 | const Charity = require('../models/charityModel'); 3 | 4 | // Create a new charity 5 | exports.createCharity = async (req, res) => { 6 | const { name, description } = req.body; 7 | 8 | // Validate input 9 | if (!name || !description) { 10 | return res.status(400).send({ message: 'Name and description are required.' }); 11 | } 12 | 13 | try { 14 | // Create a new charity 15 | const newCharity = new Charity({ name, description }); 16 | await newCharity.save(); 17 | 18 | // Respond with success 19 | res.status(201).send({ message: 'Charity created successfully', charity: newCharity }); 20 | } catch (error) { 21 | console.error('Charity creation error:', error); 22 | res.status(500).send({ message: 'Charity creation failed', error }); 23 | } 24 | }; 25 | 26 | // Get all charities 27 | exports.getAllCharities = async (req, res) => { 28 | try { 29 | const charities = await Charity.find(); 30 | res.status(200).send(charities); 31 | } catch (error) { 32 | console.error('Error retrieving charities:', error); 33 | res.status(500).send({ message: 'Failed to retrieve charities', error }); 34 | } 35 | }; 36 | 37 | // Get a specific charity by ID 38 | exports.getCharityById = async (req, res) => { 39 | const { charityId } = req.params; 40 | 41 | try { 42 | const charity = await Charity.findById(charityId); 43 | if (!charity) { 44 | return res.status(404).send({ message: 'Charity not found' }); 45 | } 46 | res.status(200).send(charity); 47 | } catch (error) { 48 | console.error('Error retrieving charity:', error); 49 | res.status(500).send({ message: 'Failed to retrieve charity', error }); 50 | } 51 | }; 52 | 53 | // Update a charity by ID 54 | exports.updateCharity = async (req, res) => { 55 | const { charityId } = req.params; 56 | const { name, description } = req.body; 57 | 58 | try { 59 | const updatedCharity = await Charity.findByIdAndUpdate( 60 | charityId, 61 | { name, description }, 62 | { new: true, runValidators: true } 63 | ); 64 | 65 | if (!updatedCharity) { 66 | return res.status(404).send({ message: 'Charity not found' }); 67 | } 68 | 69 | res.status(200).send({ message: 'Charity updated successfully', charity: updatedCharity }); 70 | } catch (error) { 71 | console.error('Charity update error:', error); 72 | res.status(500).send({ message: 'Charity update failed', error }); 73 | } 74 | }; 75 | 76 | // Delete a charity by ID 77 | exports.deleteCharity = async (req, res) => { 78 | const { charityId } = req.params; 79 | 80 | try { 81 | const deletedCharity = await Charity.findByIdAndDelete(charityId); 82 | if (!deletedCharity) { 83 | return res.status(404).send({ message: 'Charity not found' }); 84 | } 85 | res.status(200).send({ message: 'Charity deleted successfully' }); 86 | } catch (error) { 87 | console.error('Charity deletion error:', error); 88 | res.status(500).send({ message: 'Charity deletion failed', error }); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /src/backend/controllers/fraudDetectionController.js: -------------------------------------------------------------------------------- 1 | // fraudDetectionController.js 2 | 3 | const FraudDetectionService = require('../services/fraudService'); 4 | const UserService = require('../services/userService'); 5 | const { validateFraudReport } = require('../middleware/inputValidationMiddleware'); 6 | 7 | // Fraud Detection Controller for handling fraudulent activities 8 | 9 | class FraudDetectionController { 10 | // Report potential fraud 11 | async reportFraud(req, res) { 12 | try { 13 | const { userId, transactionId, reason } = req.body; 14 | validateFraudReport(req.body); // Validate input 15 | 16 | // Log the fraud report 17 | const report = await FraudDetectionService.logFraudReport(userId, transactionId, reason); 18 | return res.status(201).json({ success: true, report }); 19 | } catch (error) { 20 | console.error('Error reporting fraud:', error); 21 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 22 | } 23 | } 24 | 25 | // Analyze transactions for potential fraud 26 | async analyzeTransactions(req, res) { 27 | try { 28 | const transactions = await FraudDetectionService.getRecentTransactions(); 29 | const fraudAlerts = await FraudDetectionService.detectFraud(transactions); 30 | return res.status(200).json({ success: true, fraudAlerts }); 31 | } catch (error) { 32 | console.error('Error analyzing transactions:', error); 33 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 34 | } 35 | } 36 | 37 | // Get fraud reports for a user 38 | async getUser FraudReports(req, res) { 39 | try { 40 | const userId = req.user.id; // Assuming user ID is available in the request 41 | const reports = await FraudDetectionService.getFraudReportsByUser (userId); 42 | return res.status(200).json({ success: true, reports }); 43 | } catch (error) { 44 | console.error('Error fetching user fraud reports:', error); 45 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 46 | } 47 | } 48 | 49 | // Review and take action on fraud alerts 50 | async reviewFraudAlert(req, res) { 51 | try { 52 | const { alertId, action } = req.body; // Action could be 'investigate', 'dismiss', etc. 53 | const result = await FraudDetectionService.takeActionOnFraudAlert(alertId, action); 54 | return res.status(200).json({ success: true, result }); 55 | } catch (error) { 56 | console.error('Error reviewing fraud alert:', error); 57 | return res.status(500).json({ success: false, message: 'Internal Server Error' }); 58 | } 59 | } 60 | } 61 | 62 | module.exports = new FraudDetectionController(); 63 | -------------------------------------------------------------------------------- /src/backend/controllers/notificationController.js: -------------------------------------------------------------------------------- 1 | // src/backend/controllers/notificationController.js 2 | const Notification = require('../models/notificationModel'); 3 | 4 | // Send a notification 5 | exports.sendNotification = async (req, res) => { 6 | const { userId, message } = req.body; 7 | 8 | // Validate input 9 | if (!userId || !message) { 10 | return res.status(400).send({ message: 'User ID and message are required.' }); 11 | } 12 | 13 | try { 14 | // Create a new notification 15 | const newNotification = new Notification({ userId, message }); 16 | await newNotification.save(); 17 | 18 | // Respond with success 19 | res.status(201).send({ message: 'Notification sent successfully', notification: newNotification }); 20 | } catch (error) { 21 | console.error('Error sending notification:', error); 22 | res.status(500).send({ message: 'Failed to send notification', error }); 23 | } 24 | }; 25 | 26 | // Get notifications for a specific user 27 | exports.getUser Notifications = async (req, res) => { 28 | const { userId } = req.params; 29 | 30 | try { 31 | // Retrieve notifications for the user 32 | const notifications = await Notification.find({ userId }).sort({ createdAt: -1 }); // Sort by most recent 33 | res.status(200).send(notifications); 34 | } catch (error) { 35 | console.error('Error retrieving user notifications:', error); 36 | res.status(500).send({ message: 'Failed to retrieve notifications', error }); 37 | } 38 | }; 39 | 40 | // Mark a notification as read 41 | exports.markNotificationAsRead = async (req, res) => { 42 | const { notificationId } = req.params; 43 | 44 | try { 45 | const updatedNotification = await Notification.findByIdAndUpdate( 46 | notificationId, 47 | { isRead: true }, 48 | { new: true } 49 | ); 50 | 51 | if (!updatedNotification) { 52 | return res.status(404).send({ message: 'Notification not found' }); 53 | } 54 | 55 | res.status(200).send({ message: 'Notification marked as read', notification: updatedNotification }); 56 | } catch (error) { 57 | console.error('Error marking notification as read:', error); 58 | res.status(500).send({ message: 'Failed to mark notification as read', error }); 59 | } 60 | }; 61 | 62 | // Delete a notification 63 | exports.deleteNotification = async (req, res) => { 64 | const { notificationId } = req.params; 65 | 66 | try { 67 | const deletedNotification = await Notification.findByIdAndDelete(notificationId); 68 | if (!deletedNotification) { 69 | return res.status(404).send({ message: 'Notification not found' }); 70 | } 71 | res.status(200).send({ message: 'Notification deleted successfully' }); 72 | } catch (error) { 73 | console.error('Error deleting notification:', error); 74 | res.status(500).send({ message: 'Failed to delete notification', error }); 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /src/backend/controllers/paymentController.js: -------------------------------------------------------------------------------- 1 | // src/backend/controllers/paymentController.js 2 | const Payment = require('../models/transactionModel'); 3 | const User = require('../models/userModel'); // Assuming you have a User model for user validation 4 | 5 | // Process a payment 6 | exports.processPayment = async (req, res) => { 7 | const { userId, amount } = req.body; 8 | 9 | // Validate input 10 | if (!userId || !amount) { 11 | return res.status(400).send({ message: 'User ID and amount are required.' }); 12 | } 13 | 14 | try { 15 | // Check if user exists 16 | const user = await User.findById(userId); 17 | if (!user) { 18 | return res.status(404).send({ message: 'User not found.' }); 19 | } 20 | 21 | // Create a new payment transaction 22 | const newPayment = new Payment({ userId, amount }); 23 | await newPayment.save(); 24 | 25 | // Respond with success 26 | res.status(201).send({ message: 'Payment processed successfully', payment: newPayment }); 27 | } catch (error) { 28 | console.error('Payment processing error:', error); 29 | res.status(500).send({ message: 'Payment processing failed', error }); 30 | } 31 | }; 32 | 33 | // Get payment history for a user 34 | exports.getPaymentHistory = async (req, res) => { 35 | const { userId } = req.params; 36 | 37 | try { 38 | // Retrieve payment history for the user 39 | const payments = await Payment.find({ userId }).sort({ createdAt: -1 }); // Sort by most recent 40 | res.status(200).send(payments); 41 | } catch (error) { 42 | console.error('Error retrieving payment history:', error); 43 | res.status(500).send({ message: 'Failed to retrieve payment history', error }); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /src/backend/controllers/userController.js: -------------------------------------------------------------------------------- 1 | // src/backend/controllers/userController.js 2 | const User = require('../models/userModel'); 3 | const bcrypt = require('bcrypt'); 4 | const jwt = require('jsonwebtoken'); 5 | 6 | // Register a new user 7 | exports.registerUser = async (req, res) => { 8 | const { username, password } = req.body; 9 | 10 | // Validate input 11 | if (!username || !password) { 12 | return res.status(400).send({ message: 'Username and password are required.' }); 13 | } 14 | 15 | try { 16 | // Check if the user already exists 17 | const existingUser = await User.findOne({ username }); 18 | if (existingUser ) { 19 | return res.status(400).send({ message: 'Username already taken.' }); 20 | } 21 | 22 | // Hash the password 23 | const hashedPassword = await bcrypt.hash(password, 10); 24 | 25 | // Create a new user 26 | const newUser = new User({ username, password: hashedPassword }); 27 | await newUser .save(); 28 | 29 | // Respond with success 30 | res.status(201).send({ message: 'User registered successfully', user: newUser }); 31 | } catch (error) { 32 | console.error('User registration error:', error); 33 | res.status(500).send({ message: 'User registration failed', error }); 34 | } 35 | }; 36 | 37 | // Login a user 38 | exports.loginUser = async (req, res) => { 39 | const { username, password } = req.body; 40 | 41 | // Validate input 42 | if (!username || !password) { 43 | return res.status(400).send({ message: 'Username and password are required.' }); 44 | } 45 | 46 | try { 47 | // Find the user by username 48 | const user = await User.findOne({ username }); 49 | if (!user) { 50 | return res.status(401).send({ message: 'Invalid credentials' }); 51 | } 52 | 53 | // Compare the password with the hashed password 54 | const isMatch = await bcrypt.compare(password, user.password); 55 | if (!isMatch) { 56 | return res.status(401).send({ message: 'Invalid credentials' }); 57 | } 58 | 59 | // Generate a JWT token 60 | const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); 61 | 62 | // Respond with success 63 | res.status(200).send({ message: 'Login successful', token }); 64 | } catch (error) { 65 | console.error('User login error:', error); 66 | res.status(500).send({ message: 'Login failed', error }); 67 | } 68 | }; 69 | 70 | // Get user information 71 | exports.getUser Info = async (req, res) => { 72 | const { userId } = req.params; 73 | 74 | try { 75 | // Find the user by ID 76 | const user = await User.findById(userId).select('-password'); // Exclude password from response 77 | if (!user) { 78 | return res.status(404).send({ message: 'User not found' }); 79 | } 80 | 81 | // Respond with user information 82 | res.status(200).send(user); 83 | } catch (error) { 84 | console.error('Error retrieving user information:', error); 85 | res.status(500).send({ message: 'Failed to retrieve user information', error }); 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /src/backend/middleware/authMiddleware.js: -------------------------------------------------------------------------------- 1 | // src/backend/middleware/authMiddleware.js 2 | const jwt = require('jsonwebtoken'); 3 | 4 | // Middleware to authenticate user using JWT 5 | const authMiddleware = (req, res, next) => { 6 | // Get the token from the Authorization header 7 | const token = req.header('Authorization')?.replace('Bearer ', ''); 8 | 9 | // Check if token is provided 10 | if (!token) { 11 | return res.status(401).send({ message: 'Access denied. No token provided.' }); 12 | } 13 | 14 | try { 15 | // Verify the token 16 | const decoded = jwt.verify(token, process.env.JWT_SECRET); 17 | req.user = decoded; // Attach the decoded user information to the request object 18 | next(); // Proceed to the next middleware or route handler 19 | } catch (error) { 20 | console.error('Token verification error:', error); 21 | res.status(400).send({ message: 'Invalid token.' }); 22 | } 23 | }; 24 | 25 | module.exports = authMiddleware; 26 | -------------------------------------------------------------------------------- /src/backend/middleware/errorMiddleware.js: -------------------------------------------------------------------------------- 1 | // src/backend/middleware/errorMiddleware.js 2 | 3 | // Error handling middleware 4 | const errorMiddleware = (err, req, res, next) => { 5 | // Log the error for debugging 6 | console.error('Error:', err); 7 | 8 | // Set the response status code based on the error type 9 | const statusCode = err.statusCode || 500; // Default to 500 if no status code is set 10 | const message = err.message || 'Internal Server Error'; 11 | 12 | // Send the error response 13 | res.status(statusCode).send({ 14 | status: 'error', 15 | statusCode, 16 | message, 17 | }); 18 | }; 19 | 20 | module.exports = errorMiddleware; 21 | -------------------------------------------------------------------------------- /src/backend/middleware/loggingMiddleware.js: -------------------------------------------------------------------------------- 1 | // loggingMiddleware.js 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const morgan = require('morgan'); 6 | 7 | // Create a write stream (in append mode) for logging to a file 8 | const logStream = fs.createWriteStream(path.join(__dirname, '../logs/requests.log'), { flags: 'a' }); 9 | 10 | // Setup morgan to log requests to the console and to a file 11 | const requestLogger = morgan('combined', { stream: logStream }); 12 | 13 | // Middleware function to log requests 14 | const logRequests = (req, res, next) => { 15 | const { method, url, headers } = req; 16 | const startTime = Date.now(); 17 | 18 | // Log the request details 19 | console.log(`[${new Date().toISOString()}] ${method} ${url} - Headers: ${JSON.stringify(headers)}`); 20 | 21 | // Listen for the response to log when it's finished 22 | res.on('finish', () => { 23 | const duration = Date.now() - startTime; 24 | console.log(`[${new Date().toISOString()}] ${method} ${url} - Status: ${res.statusCode} - Duration: ${duration}ms`); 25 | }); 26 | 27 | next(); // Call the next middleware or route handler 28 | }; 29 | 30 | // Export the middleware 31 | module.exports = { 32 | requestLogger, 33 | logRequests, 34 | }; 35 | -------------------------------------------------------------------------------- /src/backend/middleware/rateLimitMiddleware.js: -------------------------------------------------------------------------------- 1 | // src/backend/middleware/rateLimitMiddleware.js 2 | const rateLimit = require('express-rate-limit'); 3 | 4 | // Create a rate limiter 5 | const limiter = rateLimit({ 6 | windowMs: 15 * 60 * 1000, // 15 minutes 7 | max: 100, // Limit each IP to 100 requests per windowMs 8 | message: { 9 | status: 'error', 10 | message: 'Too many requests, please try again later.', 11 | }, 12 | }); 13 | 14 | // Export the rate limiting middleware 15 | module.exports = limiter; 16 | -------------------------------------------------------------------------------- /src/backend/models/aiModel.js: -------------------------------------------------------------------------------- 1 | // aiModel.js 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | // Define the schema for AI recommendations 6 | const aiSchema = new mongoose.Schema({ 7 | userId: { 8 | type: mongoose.Schema.Types.ObjectId, 9 | ref: 'User ', // Reference to the User model 10 | required: true, 11 | }, 12 | preferences: { 13 | type: Map, 14 | of: String, // Key-value pairs for user preferences (e.g., category: 'sports', genre: 'action') 15 | }, 16 | interactionHistory: [ 17 | { 18 | itemId: { 19 | type: mongoose.Schema.Types.ObjectId, 20 | ref: 'Item', // Reference to the Item model (could be products, articles, etc.) 21 | required: true, 22 | }, 23 | interactionType: { 24 | type: String, 25 | enum: ['viewed', 'liked', 'shared', 'commented'], // Types of interactions 26 | required: true, 27 | }, 28 | timestamp: { 29 | type: Date, 30 | default: Date.now, // Timestamp of the interaction 31 | }, 32 | }, 33 | ], 34 | recommendations: [ 35 | { 36 | itemId: { 37 | type: mongoose.Schema.Types.ObjectId, 38 | ref: 'Item', // Reference to the Item model 39 | required: true, 40 | }, 41 | score: { 42 | type: Number, 43 | required: true, // Score indicating the strength of the recommendation 44 | }, 45 | createdAt: { 46 | type: Date, 47 | default: Date.now, // Timestamp of when the recommendation was generated 48 | }, 49 | }, 50 | ], 51 | }); 52 | 53 | // Create the AI model 54 | const AIModel = mongoose.model('AI', aiSchema); 55 | 56 | module.exports = AIModel; 57 | -------------------------------------------------------------------------------- /src/backend/models/analyticsModel.js: -------------------------------------------------------------------------------- 1 | // src/backend/models/analyticsModel.js 2 | const mongoose = require('mongoose'); 3 | 4 | // Define the analytics schema 5 | const analyticsSchema = new mongoose.Schema({ 6 | totalUsers: { 7 | type: Number, 8 | required: true 9 | }, 10 | totalPayments: { 11 | type: Number, 12 | required: true 13 | }, 14 | totalPaymentAmount: { 15 | type: Number, 16 | required: true 17 | }, 18 | totalCharities: { 19 | type: Number, 20 | required: true 21 | }, 22 | createdAt: { 23 | type: Date, 24 | default: Date.now 25 | } 26 | }); 27 | 28 | // Method to update analytics data 29 | analyticsSchema.methods.updateAnalytics = function(data) { 30 | this.totalUsers = data.totalUsers || this.totalUsers; 31 | this.totalPayments = data.totalPayments || this.totalPayments; 32 | this.totalPaymentAmount = data.totalPaymentAmount || this.totalPaymentAmount; 33 | this.totalCharities = data.totalCharities || this.totalCharities; 34 | return this.save(); 35 | }; 36 | 37 | // Export the Analytics model 38 | module.exports = mongoose.model('Analytics', analyticsSchema); 39 | -------------------------------------------------------------------------------- /src/backend/models/charityModel.js: -------------------------------------------------------------------------------- 1 | // src/backend/models/charityModel.js 2 | const mongoose = require('mongoose'); 3 | 4 | // Define the charity schema 5 | const charitySchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | required: true, 9 | trim: true 10 | }, 11 | description: { 12 | type: String, 13 | required: true 14 | }, 15 | createdAt: { 16 | type: Date, 17 | default: Date.now 18 | }, 19 | totalDonations: { 20 | type: Number, 21 | default: 0 22 | }, 23 | isActive: { 24 | type: Boolean, 25 | default: true 26 | } 27 | }); 28 | 29 | // Method to update total donations 30 | charitySchema.methods.updateTotalDonations = function(amount) { 31 | this.totalDonations += amount; 32 | return this.save(); 33 | }; 34 | 35 | // Export the Charity model 36 | module.exports = mongoose.model('Charity', charitySchema); 37 | -------------------------------------------------------------------------------- /src/backend/models/communityModel.js: -------------------------------------------------------------------------------- 1 | // communityModel.js 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | // Define the schema for community engagement data 6 | const communitySchema = new mongoose.Schema({ 7 | userId: { 8 | type: mongoose.Schema.Types.ObjectId, 9 | ref: 'User ', // Reference to the User model 10 | required: true, 11 | }, 12 | title: { 13 | type: String, 14 | required: true, // Title of the post 15 | trim: true, 16 | }, 17 | content: { 18 | type: String, 19 | required: true, // Content of the post 20 | trim: true, 21 | }, 22 | createdAt: { 23 | type: Date, 24 | default: Date.now, // Timestamp of when the post was created 25 | }, 26 | updatedAt: { 27 | type: Date, 28 | default: Date.now, // Timestamp of the last update to the post 29 | }, 30 | likes: [ 31 | { 32 | type: mongoose.Schema.Types.ObjectId, 33 | ref: 'User ', // Users who liked the post 34 | }, 35 | ], 36 | comments: [ 37 | { 38 | userId: { 39 | type: mongoose.Schema.Types.ObjectId, 40 | ref: 'User ', // Reference to the User model who commented 41 | required: true, 42 | }, 43 | content: { 44 | type: String, 45 | required: true, // Content of the comment 46 | trim: true, 47 | }, 48 | createdAt: { 49 | type: Date, 50 | default: Date.now, // Timestamp of when the comment was created 51 | }, 52 | }, 53 | ], 54 | }); 55 | 56 | // Create the Community model 57 | const CommunityModel = mongoose.model('Community', communitySchema); 58 | 59 | module.exports = CommunityModel; 60 | -------------------------------------------------------------------------------- /src/backend/models/fraudModel.js: -------------------------------------------------------------------------------- 1 | // fraudModel.js 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | // Define the schema for fraud detection data 6 | const fraudSchema = new mongoose.Schema({ 7 | transactionId: { 8 | type: mongoose.Schema.Types.ObjectId, 9 | ref: 'Transaction', // Reference to the Transaction model 10 | required: true, 11 | }, 12 | userId: { 13 | type: mongoose.Schema.Types.ObjectId, 14 | ref: 'User ', // Reference to the User model 15 | required: true, 16 | }, 17 | reportedBy: { 18 | type: mongoose.Schema.Types.ObjectId, 19 | ref: 'User ', // Reference to the User model who reported the fraud 20 | required: true, 21 | }, 22 | reason: { 23 | type: String, 24 | required: true, // Reason for reporting the fraud 25 | }, 26 | status: { 27 | type: String, 28 | enum: ['pending', 'investigating', 'resolved', 'dismissed'], // Status of the fraud report 29 | default: 'pending', 30 | }, 31 | createdAt: { 32 | type: Date, 33 | default: Date.now, // Timestamp of when the fraud report was created 34 | }, 35 | updatedAt: { 36 | type: Date, 37 | default: Date.now, // Timestamp of the last update to the fraud report 38 | }, 39 | fraudScore: { 40 | type: Number, 41 | default: 0, // Score indicating the likelihood of fraud (can be calculated based on various factors) 42 | }, 43 | additionalInfo: { 44 | type: Map, 45 | of: String, // Additional information related to the fraud case (e.g., IP address, device info) 46 | }, 47 | }); 48 | 49 | // Create the Fraud model 50 | const FraudModel = mongoose.model('Fraud', fraudSchema); 51 | 52 | module.exports = FraudModel; 53 | -------------------------------------------------------------------------------- /src/backend/models/notificationModel.js: -------------------------------------------------------------------------------- 1 | // src/backend/models/notificationModel.js 2 | const mongoose = require('mongoose'); 3 | 4 | // Define the notification schema 5 | const notificationSchema = new mongoose.Schema({ 6 | userId: { 7 | type: mongoose.Schema.Types.ObjectId, 8 | ref: 'User ', 9 | required: true 10 | }, 11 | message: { 12 | type: String, 13 | required: true 14 | }, 15 | createdAt: { 16 | type: Date, 17 | default: Date.now 18 | }, 19 | isRead: { 20 | type: Boolean, 21 | default: false 22 | } 23 | }); 24 | 25 | // Method to mark notification as read 26 | notificationSchema.methods.markAsRead = function() { 27 | this.isRead = true; 28 | return this.save(); 29 | }; 30 | 31 | // Export the Notification model 32 | module.exports = mongoose.model('Notification', notificationSchema); 33 | -------------------------------------------------------------------------------- /src/backend/models/transactionModel.js: -------------------------------------------------------------------------------- 1 | // src/backend/models/transactionModel.js 2 | const mongoose = require('mongoose'); 3 | 4 | // Define the transaction schema 5 | const transactionSchema = new mongoose.Schema({ 6 | userId: { 7 | type: mongoose.Schema.Types.ObjectId, 8 | ref: 'User ', 9 | required: true 10 | }, 11 | amount: { 12 | type: Number, 13 | required: true 14 | }, 15 | createdAt: { 16 | type: Date, 17 | default: Date.now 18 | }, 19 | status: { 20 | type: String, 21 | enum: ['pending', 'completed', 'failed'], 22 | default: 'pending' 23 | }, 24 | description: { 25 | type: String 26 | } 27 | }); 28 | 29 | // Method to update the transaction status 30 | transactionSchema.methods.updateStatus = function(newStatus) { 31 | this.status = newStatus; 32 | return this.save(); 33 | }; 34 | 35 | // Export the Transaction model 36 | module.exports = mongoose.model('Transaction', transactionSchema); 37 | -------------------------------------------------------------------------------- /src/backend/models/userModel.js: -------------------------------------------------------------------------------- 1 | // src/backend/models/userModel.js 2 | const mongoose = require('mongoose'); 3 | 4 | // Define the user schema 5 | const userSchema = new mongoose.Schema({ 6 | username: { 7 | type: String, 8 | required: true, 9 | unique: true, 10 | trim: true 11 | }, 12 | password: { 13 | type: String, 14 | required: true 15 | }, 16 | email: { 17 | type: String, 18 | required: true, 19 | unique: true, 20 | trim: true 21 | }, 22 | createdAt: { 23 | type: Date, 24 | default: Date.now 25 | }, 26 | lastLogin: { 27 | type: Date 28 | }, 29 | isActive: { 30 | type: Boolean, 31 | default: true 32 | } 33 | }); 34 | 35 | // Method to update the last login time 36 | userSchema.methods.updateLastLogin = function() { 37 | this.lastLogin = Date.now(); 38 | return this.save(); 39 | }; 40 | 41 | // Export the User model 42 | module.exports = mongoose.model('User ', userSchema); 43 | -------------------------------------------------------------------------------- /src/backend/routes/aiRoutes.js: -------------------------------------------------------------------------------- 1 | // aiRoutes.js 2 | 3 | const express = require('express'); 4 | const router = express.Router(); 5 | const AIController = require('../controllers/aiController'); 6 | const { authMiddleware } = require('../middleware/authMiddleware'); 7 | const { validateRecommendationInput, validateFeedbackInput } = require('../middleware/inputValidationMiddleware'); 8 | 9 | // Route to get personalized recommendations for a user 10 | router.get('/recommendations', authMiddleware, async (req, res) => { 11 | try { 12 | await AIController.getPersonalizedRecommendations(req, res); 13 | } catch (error) { 14 | res.status(500).json({ success: false, message: 'Error fetching recommendations' }); 15 | } 16 | }); 17 | 18 | // Route to analyze user feedback for sentiment 19 | router.post('/feedback/analyze', authMiddleware, validateFeedbackInput, async (req, res) => { 20 | try { 21 | await AIController.analyzeUser Feedback(req, res); 22 | } catch (error) { 23 | res.status(500).json({ success: false, message: 'Error analyzing feedback' }); 24 | } 25 | }); 26 | 27 | // Route to get user behavior analysis 28 | router.get('/behavior/analysis', authMiddleware, async (req, res) => { 29 | try { 30 | await AIController.getUser BehaviorAnalysis(req, res); 31 | } catch (error) { 32 | res.status(500).json({ success: false, message: 'Error fetching user behavior analysis' }); 33 | } 34 | }); 35 | 36 | // Route to get community engagement analysis 37 | router.get('/community/engagement/:communityId', authMiddleware, async (req, res) => { 38 | try { 39 | await AIController.getCommunityEngagementAnalysis(req, res); 40 | } catch (error) { 41 | res.status(500).json({ success: false, message: 'Error fetching community engagement analysis' }); 42 | } 43 | }); 44 | 45 | // Route to get AI-driven insights 46 | router.get('/insights', authMiddleware, async (req, res) => { 47 | try { 48 | const insights = await AIController.getAIInsights(req.user.id); // Assuming user ID is available 49 | res.status(200).json({ success: true, insights }); 50 | } catch (error) { 51 | res.status(500).json({ success: false, message: 'Error fetching AI insights' }); 52 | } 53 | }); 54 | 55 | // Route to update user preferences for recommendations 56 | router.put('/preferences', authMiddleware, validateRecommendationInput, async (req, res) => { 57 | try { 58 | const updatedPreferences = await AIController.updateUser Preferences(req.user.id, req.body); 59 | res.status(200).json({ success: true, updatedPreferences }); 60 | } catch (error) { 61 | res.status(500).json({ success: false, message: 'Error updating preferences' }); 62 | } 63 | }); 64 | 65 | module.exports = router; 66 | -------------------------------------------------------------------------------- /src/backend/routes/analyticsRoutes.js: -------------------------------------------------------------------------------- 1 | // src/backend/routes/analyticsRoutes.js 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const analyticsController = require('../controllers/analyticsController'); 5 | 6 | // Route to get overall analytics data 7 | router.get('/', analyticsController.getAnalyticsData); 8 | 9 | // Route to get payment analytics for a specific user 10 | router.get('/user/:userId', analyticsController.getUser PaymentAnalytics); 11 | 12 | // Route to get charity analytics 13 | router.get('/charity', analyticsController.getCharityAnalytics); 14 | 15 | // Export the analytics routes 16 | module.exports = router; 17 | -------------------------------------------------------------------------------- /src/backend/routes/charityRoutes.js: -------------------------------------------------------------------------------- 1 | // src/backend/routes/charityRoutes.js 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const charityController = require('../controllers/charityController'); 5 | 6 | // Route to create a new charity 7 | router.post('/', charityController.createCharity); 8 | 9 | // Route to get all charities 10 | router.get('/', charityController.getAllCharities); 11 | 12 | // Route to get a specific charity by ID 13 | router.get('/:charityId', charityController.getCharityById); 14 | 15 | // Route to update a charity by ID 16 | router.put('/:charityId', charityController.updateCharity); 17 | 18 | // Route to delete a charity by ID 19 | router.delete('/:charityId', charityController.deleteCharity); 20 | 21 | // Export the charity routes 22 | module.exports = router; 23 | -------------------------------------------------------------------------------- /src/backend/routes/communityRoutes.js: -------------------------------------------------------------------------------- 1 | // communityRoutes.js 2 | 3 | const express = require('express'); 4 | const router = express.Router(); 5 | const CommunityController = require('../controllers/communityController'); 6 | const { authMiddleware } = require('../middleware/authMiddleware'); 7 | const { validatePostInput, validateCommentInput } = require('../middleware/inputValidationMiddleware'); 8 | 9 | // Route to create a new post 10 | router.post('/posts', authMiddleware, validatePostInput, async (req, res) => { 11 | try { 12 | await CommunityController.createPost(req, res); 13 | } catch (error) { 14 | res.status(500).json({ success: false, message: 'Error creating post' }); 15 | } 16 | }); 17 | 18 | // Route to get all posts 19 | router.get('/posts', authMiddleware, async (req, res) => { 20 | try { 21 | await CommunityController.getAllPosts(req, res); 22 | } catch (error) { 23 | res.status(500).json({ success: false, message: 'Error fetching posts' }); 24 | } 25 | }); 26 | 27 | // Route to get a single post by ID 28 | router.get('/posts/:postId', authMiddleware, async (req, res) => { 29 | try { 30 | await CommunityController.getPostById(req, res); 31 | } catch (error) { 32 | res.status(500).json({ success: false, message: 'Error fetching post' }); 33 | } 34 | }); 35 | 36 | // Route to create a comment on a post 37 | router.post('/posts/:postId/comments', authMiddleware, validateCommentInput, async (req, res) => { 38 | try { 39 | await CommunityController.createComment(req, res); 40 | } catch (error) { 41 | res.status(500).json({ success: false, message: 'Error creating comment' }); 42 | } 43 | }); 44 | 45 | // Route to get comments for a specific post 46 | router.get('/posts/:postId/comments', authMiddleware, async (req, res) => { 47 | try { 48 | await CommunityController.getCommentsForPost(req, res); 49 | } catch (error) { 50 | res.status(500).json({ success: false, message: 'Error fetching comments' }); 51 | } 52 | }); 53 | 54 | // Route to like a post 55 | router.post('/posts/:postId/like', authMiddleware, async (req, res) => { 56 | try { 57 | await CommunityController.likePost(req, res); 58 | } catch (error) { 59 | res.status(500).json({ success: false, message: 'Error liking post' }); 60 | } 61 | }); 62 | 63 | // Route to dislike a post 64 | router.post('/posts/:postId/dislike', authMiddleware, async (req, res) => { 65 | try { 66 | await CommunityController.dislikePost(req, res); 67 | } catch (error) { 68 | res.status(500).json({ success: false, message: 'Error disliking post' }); 69 | } 70 | }); 71 | 72 | module.exports = router; 73 | -------------------------------------------------------------------------------- /src/backend/routes/eventRoutes.js: -------------------------------------------------------------------------------- 1 | // eventRoutes.js 2 | 3 | const express = require('express'); 4 | const router = express.Router(); 5 | const EventController = require('../controllers/eventController'); 6 | const { authMiddleware } = require('../middleware/authMiddleware'); 7 | const { validateEventInput, validateRegistrationInput } = require('../middleware/inputValidationMiddleware'); 8 | 9 | // Route to create a new event 10 | router.post('/events', authMiddleware, validateEventInput, async (req, res) => { 11 | try { 12 | await EventController.createEvent(req, res); 13 | } catch (error) { 14 | res.status(500).json({ success: false, message: 'Error creating event' }); 15 | } 16 | }); 17 | 18 | // Route to get all events 19 | router.get('/events', authMiddleware, async (req, res) => { 20 | try { 21 | await EventController.getAllEvents(req, res); 22 | } catch (error) { 23 | res.status(500).json({ success: false, message: 'Error fetching events' }); 24 | } 25 | }); 26 | 27 | // Route to get a single event by ID 28 | router.get('/events/:eventId', authMiddleware, async (req, res) => { 29 | try { 30 | await EventController.getEventById(req, res); 31 | } catch (error) { 32 | res.status(500).json({ success: false, message: 'Error fetching event' }); 33 | } 34 | }); 35 | 36 | // Route to register for an event 37 | router.post('/events/:eventId/register', authMiddleware, validateRegistrationInput, async (req, res) => { 38 | try { 39 | await EventController.registerForEvent(req, res); 40 | } catch (error) { 41 | res.status(500).json({ success: false, message: 'Error registering for event' }); 42 | } 43 | }); 44 | 45 | // Route to get registrations for a specific event 46 | router.get('/events/:eventId/registrations', authMiddleware, async (req, res) => { 47 | try { 48 | await EventController.getEventRegistrations(req, res); 49 | } catch (error) { 50 | res.status(500).json({ success: false, message: 'Error fetching event registrations' }); 51 | } 52 | }); 53 | 54 | // Route to cancel registration for an event 55 | router.delete('/events/:eventId/register', authMiddleware, async (req, res) => { 56 | try { 57 | await EventController.cancelRegistration(req, res); 58 | } catch (error) { 59 | res.status(500).json({ success: false, message: 'Error canceling registration' }); 60 | } 61 | }); 62 | 63 | module.exports = router; 64 | -------------------------------------------------------------------------------- /src/backend/routes/fraudRoutes.js: -------------------------------------------------------------------------------- 1 | // fraudRoutes.js 2 | 3 | const express = require('express'); 4 | const router = express.Router(); 5 | const FraudDetectionController = require('../controllers/fraudDetectionController'); 6 | const { authMiddleware } = require('../middleware/authMiddleware'); 7 | const { validateFraudReportInput } = require('../middleware/inputValidationMiddleware'); 8 | 9 | // Route to report potential fraud 10 | router.post('/report', authMiddleware, validateFraudReportInput, async (req, res) => { 11 | try { 12 | await FraudDetectionController.reportFraud(req, res); 13 | } catch (error) { 14 | res.status(500).json({ success: false, message: 'Error reporting fraud' }); 15 | } 16 | }); 17 | 18 | // Route to analyze recent transactions for potential fraud 19 | router.get('/analyze', authMiddleware, async (req, res) => { 20 | try { 21 | await FraudDetectionController.analyzeTransactions(req, res); 22 | } catch (error) { 23 | res.status(500).json({ success: false, message: 'Error analyzing transactions' }); 24 | } 25 | }); 26 | 27 | // Route to get fraud reports for a user 28 | router.get('/reports', authMiddleware, async (req, res) => { 29 | try { 30 | await FraudDetectionController.getUser FraudReports(req, res); 31 | } catch (error) { 32 | res.status(500).json({ success: false, message: 'Error fetching fraud reports' }); 33 | } 34 | }); 35 | 36 | // Route to review and take action on a fraud alert 37 | router.put('/alerts/:alertId', authMiddleware, async (req, res) => { 38 | try { 39 | await FraudDetectionController.reviewFraudAlert(req, res); 40 | } catch (error) { 41 | res.status(500).json({ success: false, message: 'Error reviewing fraud alert' }); 42 | } 43 | }); 44 | 45 | // Route to get all fraud alerts (admin only) 46 | router.get('/alerts', authMiddleware, async (req, res) => { 47 | try { 48 | // Assuming there's an admin check in the middleware 49 | await FraudDetectionController.getAllFraudAlerts(req, res); 50 | } catch (error) { 51 | res.status(500).json({ success: false, message: 'Error fetching fraud alerts' }); 52 | } 53 | }); 54 | 55 | module.exports = router; 56 | -------------------------------------------------------------------------------- /src/backend/routes/notificationRoutes.js: -------------------------------------------------------------------------------- 1 | // src/backend/routes/notificationRoutes.js 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const notificationController = require('../controllers/notificationController'); 5 | 6 | // Route to send a notification 7 | router.post('/', notificationController.sendNotification); 8 | 9 | // Route to get notifications for a specific user 10 | router.get('/user/:userId', notificationController.getUser Notifications); 11 | 12 | // Route to mark a notification as read 13 | router.put('/read/:notificationId', notificationController.markNotificationAsRead); 14 | 15 | // Route to delete a notification 16 | router.delete('/:notificationId', notificationController.deleteNotification); 17 | 18 | // Export the notification routes 19 | module.exports = router; 20 | -------------------------------------------------------------------------------- /src/backend/routes/paymentRoutes.js: -------------------------------------------------------------------------------- 1 | // src/backend/routes/paymentRoutes.js 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const paymentController = require('../controllers/paymentController'); 5 | 6 | // Route to process a payment 7 | router.post('/process', paymentController.processPayment); 8 | 9 | // Route to get payment history for a specific user 10 | router.get('/history/:userId', paymentController.getPaymentHistory); 11 | 12 | // Export the payment routes 13 | module.exports = router; 14 | -------------------------------------------------------------------------------- /src/backend/routes/userRoutes.js: -------------------------------------------------------------------------------- 1 | // src/backend/routes/userRoutes.js 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const userController = require('../controllers/userController'); 5 | 6 | // Route to register a new user 7 | router.post('/register', userController.registerUser ); 8 | 9 | // Route to log in a user 10 | router.post('/login', userController.loginUser ); 11 | 12 | // Route to get user information by user ID 13 | router.get('/:userId', userController.getUser Info); 14 | 15 | // Export the user routes 16 | module.exports = router; 17 | -------------------------------------------------------------------------------- /src/backend/server.js: -------------------------------------------------------------------------------- 1 | // src/backend/server.js 2 | const express = require('express'); 3 | const mongoose = require('mongoose'); 4 | const dotenv = require('dotenv'); 5 | const cors = require('cors'); 6 | const bodyParser = require('body-parser'); 7 | const userRoutes = require('./routes/userRoutes'); 8 | const charityRoutes = require('./routes/charityRoutes'); 9 | const analyticsRoutes = require('./routes/analyticsRoutes'); 10 | const notificationRoutes = require('./routes/notificationRoutes'); 11 | const errorMiddleware = require('./middleware/errorMiddleware'); 12 | const rateLimitMiddleware = require('./middleware/rateLimitMiddleware'); 13 | 14 | // Load environment variables from .env file 15 | dotenv.config(); 16 | 17 | // Create an Express application 18 | const app = express(); 19 | 20 | // Middleware 21 | app.use(cors()); // Enable CORS 22 | app.use(bodyParser.json()); // Parse JSON request bodies 23 | app.use(rateLimitMiddleware); // Apply rate limiting middleware 24 | 25 | // Connect to MongoDB 26 | mongoose.connect(process.env.MONGODB_URI, { 27 | useNewUrlParser: true, 28 | useUnifiedTopology: true, 29 | }) 30 | .then(() => { 31 | console.log('Connected to MongoDB'); 32 | }) 33 | .catch((error) => { 34 | console.error('MongoDB connection error:', error); 35 | }); 36 | 37 | // Define API routes 38 | app.use('/api/users', userRoutes); 39 | app.use('/api/charities', charityRoutes); 40 | app.use('/api/analytics', analyticsRoutes); 41 | app.use('/api/notifications', notificationRoutes); 42 | 43 | // Error handling middleware 44 | app.use(errorMiddleware); 45 | 46 | // Start the server 47 | const PORT = process.env.PORT || 5000; 48 | app.listen(PORT, () => { 49 | console.log(`Server is running on port ${PORT}`); 50 | }); 51 | -------------------------------------------------------------------------------- /src/backend/services/aiService.js: -------------------------------------------------------------------------------- 1 | // aiService.js 2 | 3 | const AIModel = require('../models/aiModel'); 4 | const UserModel = require('../models/userModel'); 5 | const TransactionModel = require('../models/transactionModel'); 6 | const RecommendationEngine = require('../utils/recommendationEngine'); // Hypothetical utility for generating recommendations 7 | const SentimentAnalysis = require('../utils/sentimentAnalysis'); // Hypothetical utility for sentiment analysis 8 | 9 | class AIService { 10 | // Generate personalized recommendations for a user 11 | async generateRecommendations(userId) { 12 | const userPreferences = await this.getUser Preferences(userId); 13 | const interactionHistory = await this.getUser InteractionHistory(userId); 14 | 15 | // Use a recommendation engine to generate recommendations 16 | const recommendations = RecommendationEngine.generate(userPreferences, interactionHistory); 17 | 18 | // Save recommendations to the database 19 | await this.saveRecommendations(userId, recommendations); 20 | 21 | return recommendations; 22 | } 23 | 24 | // Get user preferences from the database 25 | async getUser Preferences(userId) { 26 | const user = await UserModel.findById(userId); 27 | return user.preferences; // Assuming preferences are stored in the User model 28 | } 29 | 30 | // Get user interaction history from the database 31 | async getUser InteractionHistory(userId) { 32 | return await AIModel.findOne({ userId }).select('interactionHistory'); 33 | } 34 | 35 | // Save recommendations to the database 36 | async saveRecommendations(userId, recommendations) { 37 | const aiData = await AIModel.findOneAndUpdate( 38 | { userId }, 39 | { recommendations }, 40 | { new: true, upsert: true } // Create a new document if it doesn't exist 41 | ); 42 | return aiData; 43 | } 44 | 45 | // Analyze user feedback for sentiment 46 | async analyzeSentiment(feedback) { 47 | const sentimentScore = await SentimentAnalysis.analyze(feedback); 48 | return sentimentScore; // Return sentiment score (e.g., positive, negative, neutral) 49 | } 50 | 51 | // Analyze user behavior patterns 52 | async analyzeUser Behavior(userId) { 53 | const interactionHistory = await this.getUser InteractionHistory(userId); 54 | // Perform analysis on interaction history (e.g., clustering, trend analysis) 55 | const analysisResults = this.performBehaviorAnalysis(interactionHistory); 56 | return analysisResults; 57 | } 58 | 59 | // Perform behavior analysis (placeholder for actual analysis logic) 60 | performBehaviorAnalysis(interactionHistory) { 61 | // Implement your analysis logic here (e.g., clustering algorithms, statistical analysis) 62 | return {}; // Return analysis results 63 | } 64 | 65 | // Analyze community engagement data 66 | async analyzeCommunityEngagement(communityId) { 67 | // Fetch community engagement data (e.g., posts, comments) 68 | const engagementData = await this.getCommunityEngagementData(communityId); 69 | // Perform analysis on engagement data 70 | const analysisResults = this.performEngagementAnalysis(engagementData); 71 | return analysisResults; 72 | } 73 | 74 | // Get community engagement data (placeholder for actual data retrieval) 75 | async getCommunityEngagementData(communityId) { 76 | // Fetch data related to community engagement (e.g., posts, comments) 77 | return []; // Return engagement data 78 | } 79 | 80 | // Perform engagement analysis (placeholder for actual analysis logic) 81 | performEngagementAnalysis(engagementData) { 82 | // Implement your analysis logic here (e.g., sentiment analysis, trend analysis) 83 | return {}; // Return analysis results 84 | } 85 | } 86 | 87 | module.exports = new AIService(); 88 | -------------------------------------------------------------------------------- /src/backend/services/analyticsService.js: -------------------------------------------------------------------------------- 1 | // src/backend/services/analyticsService.js 2 | const Transaction = require('../models/transactionModel'); 3 | const User = require('../models/userModel'); 4 | const Charity = require('../models/charityModel'); 5 | const Analytics = require('../models/analyticsModel'); 6 | 7 | // Get overall analytics data 8 | const getAnalyticsData = async () => { 9 | const totalUsers = await User.countDocuments(); 10 | const totalPayments = await Transaction.countDocuments(); 11 | const totalPaymentAmount = await Transaction.aggregate([ 12 | { $group: { _id: null, total: { $sum: '$amount' } } } 13 | ]); 14 | const totalCharities = await Charity.countDocuments(); 15 | 16 | return { 17 | totalUsers, 18 | totalPayments, 19 | totalPaymentAmount: totalPaymentAmount[0]?.total || 0, 20 | totalCharities, 21 | }; 22 | }; 23 | 24 | // Get payment analytics for a specific user 25 | const getUser PaymentAnalytics = async (userId) => { 26 | // Validate input 27 | if (!userId) { 28 | throw new Error('User ID is required.'); 29 | } 30 | 31 | const totalPayments = await Transaction.countDocuments({ userId }); 32 | const totalPaymentAmount = await Transaction.aggregate([ 33 | { $match: { userId } }, 34 | { $group: { _id: null, total: { $sum: '$amount' } } } 35 | ]); 36 | 37 | return { 38 | totalPayments, 39 | totalPaymentAmount: totalPaymentAmount[0]?.total || 0, 40 | }; 41 | }; 42 | 43 | // Get charity analytics 44 | const getCharityAnalytics = async () => { 45 | const totalCharities = await Charity.countDocuments(); 46 | const totalDonations = await Transaction.aggregate([ 47 | { $group: { _id: null, total: { $sum: '$amount' } } } 48 | ]); 49 | 50 | return { 51 | totalCharities, 52 | totalDonations: totalDonations[0]?.total || 0, 53 | }; 54 | }; 55 | 56 | // Export the analytics service functions 57 | module.exports = { 58 | getAnalyticsData, 59 | getUser PaymentAnalytics, 60 | getCharityAnalytics, 61 | }; 62 | -------------------------------------------------------------------------------- /src/backend/services/charityService.js: -------------------------------------------------------------------------------- 1 | // src/backend/services/charityService.js 2 | const Charity = require('../models/charityModel'); 3 | 4 | // Create a new charity 5 | const createCharity = async (name, description) => { 6 | // Validate input 7 | if (!name || !description) { 8 | throw new Error('Name and description are required.'); 9 | } 10 | 11 | // Create a new charity 12 | const charity = new Charity({ 13 | name, 14 | description, 15 | }); 16 | 17 | // Save the charity to the database 18 | await charity.save(); 19 | 20 | return charity; 21 | }; 22 | 23 | // Get all charities 24 | const getAllCharities = async () => { 25 | // Retrieve all charities from the database 26 | const charities = await Charity.find(); 27 | return charities; 28 | }; 29 | 30 | // Get a charity by ID 31 | const getCharityById = async (charityId) => { 32 | // Validate input 33 | if (!charityId) { 34 | throw new Error('Charity ID is required.'); 35 | } 36 | 37 | // Retrieve the charity by ID 38 | const charity = await Charity.findById(charityId); 39 | if (!charity) { 40 | throw new Error('Charity not found.'); 41 | } 42 | 43 | return charity; 44 | }; 45 | 46 | // Update a charity by ID 47 | const updateCharity = async (charityId, updates) => { 48 | // Validate input 49 | if (!charityId || !updates) { 50 | throw new Error('Charity ID and updates are required.'); 51 | } 52 | 53 | // Update the charity 54 | const charity = await Charity.findByIdAndUpdate(charityId, updates, { new: true }); 55 | if (!charity) { 56 | throw new Error('Charity not found.'); 57 | } 58 | 59 | return charity; 60 | }; 61 | 62 | // Delete a charity by ID 63 | const deleteCharity = async (charityId) => { 64 | // Validate input 65 | if (!charityId) { 66 | throw new Error('Charity ID is required.'); 67 | } 68 | 69 | // Delete the charity 70 | const charity = await Charity.findByIdAndDelete(charityId); 71 | if (!charity) { 72 | throw new Error('Charity not found.'); 73 | } 74 | 75 | return charity; 76 | }; 77 | 78 | // Export the charity service functions 79 | module.exports = { 80 | createCharity, 81 | getAllCharities, 82 | getCharityById, 83 | updateCharity, 84 | deleteCharity, 85 | }; 86 | -------------------------------------------------------------------------------- /src/backend/services/communityService.js: -------------------------------------------------------------------------------- 1 | // communityService.js 2 | 3 | const PostModel = require('../models/communityModel'); // Assuming communityModel contains the Post schema 4 | const CommentModel = require('../models/commentModel'); // Assuming a separate Comment model exists 5 | const UserModel = require('../models/userModel'); // Assuming a User model exists 6 | 7 | class CommunityService { 8 | // Create a new post 9 | async createPost(userId, title, content) { 10 | const newPost = new PostModel({ 11 | userId, 12 | title, 13 | content, 14 | }); 15 | await newPost.save(); 16 | return newPost; 17 | } 18 | 19 | // Get all posts 20 | async getAllPosts() { 21 | return await PostModel.find() 22 | .populate('userId', 'username') // Populate user details 23 | .sort({ createdAt: -1 }); // Sort by creation date 24 | } 25 | 26 | // Get a single post by ID 27 | async getPostById(postId) { 28 | const post = await PostModel.findById(postId).populate('userId', 'username'); 29 | if (!post) { 30 | throw new Error('Post not found'); 31 | } 32 | return post; 33 | } 34 | 35 | // Create a comment on a post 36 | async createComment(userId, postId, content) { 37 | const newComment = new CommentModel({ 38 | userId, 39 | postId, 40 | content, 41 | }); 42 | await newComment.save(); 43 | return newComment; 44 | } 45 | 46 | // Get comments for a specific post 47 | async getCommentsForPost(postId) { 48 | return await CommentModel.find({ postId }) 49 | .populate('userId', 'username') // Populate user details 50 | .sort({ createdAt: -1 }); // Sort by creation date 51 | } 52 | 53 | // Like a post 54 | async likePost(userId, postId) { 55 | const post = await PostModel.findById(postId); 56 | if (!post) { 57 | throw new Error('Post not found'); 58 | } 59 | if (!post.likes.includes(userId)) { 60 | post.likes.push(userId); // Add user ID to likes 61 | await post.save(); 62 | return { message: 'Post liked successfully' }; 63 | } 64 | return { message: 'Post already liked' }; 65 | } 66 | 67 | // Dislike a post 68 | async dislikePost(userId, postId) { 69 | const post = await PostModel.findById(postId); 70 | if (!post) { 71 | throw new Error('Post not found'); 72 | } 73 | if (post.likes.includes(userId)) { 74 | post.likes = post.likes.filter(id => id.toString() !== userId.toString()); // Remove user ID from likes 75 | await post.save(); 76 | return { message: 'Post disliked successfully' }; 77 | } 78 | return { message: 'Post not liked yet' }; 79 | } 80 | } 81 | 82 | module.exports = new CommunityService(); 83 | -------------------------------------------------------------------------------- /src/backend/services/eventService.js: -------------------------------------------------------------------------------- 1 | // eventService.js 2 | 3 | const EventModel = require('../models/eventModel'); // Assuming eventModel contains the Event schema 4 | const RegistrationModel = require('../models/registrationModel'); // Assuming a separate Registration model exists 5 | const UserModel = require('../models/userModel'); // Assuming a User model exists 6 | 7 | class EventService { 8 | // Create a new event 9 | async createEvent(userId, title, description, date, location, capacity) { 10 | const newEvent = new EventModel({ 11 | userId, 12 | title, 13 | description, 14 | date, 15 | location, 16 | capacity, 17 | }); 18 | await newEvent.save(); 19 | return newEvent; 20 | } 21 | 22 | // Get all events 23 | async getAllEvents() { 24 | return await EventModel.find() 25 | .populate('userId', 'username') // Populate user details 26 | .sort({ date: 1 }); // Sort by event date 27 | } 28 | 29 | // Get a single event by ID 30 | async getEventById(eventId) { 31 | const event = await EventModel.findById(eventId).populate('userId', 'username'); 32 | if (!event) { 33 | throw new Error('Event not found'); 34 | } 35 | return event; 36 | } 37 | 38 | // Register for an event 39 | async registerForEvent(userId, eventId) { 40 | const event = await EventModel.findById(eventId); 41 | if (!event) { 42 | throw new Error('Event not found'); 43 | } 44 | if (event.capacity <= 0) { 45 | throw new Error('Event is fully booked'); 46 | } 47 | 48 | const registration = new RegistrationModel({ 49 | userId, 50 | eventId, 51 | }); 52 | await registration.save(); 53 | 54 | // Decrease the event capacity 55 | event.capacity -= 1; 56 | await event.save(); 57 | 58 | return registration; 59 | } 60 | 61 | // Get registrations for a specific event 62 | async getEventRegistrations(eventId) { 63 | return await RegistrationModel.find({ eventId }) 64 | .populate('userId', 'username') // Populate user details 65 | .sort({ createdAt: -1 }); // Sort by registration date 66 | } 67 | 68 | // Cancel registration for an event 69 | async cancelRegistration(userId, eventId) { 70 | const registration = await RegistrationModel.findOneAndDelete({ userId, eventId }); 71 | if (!registration) { 72 | throw new Error('Registration not found'); 73 | } 74 | 75 | // Increase the event capacity 76 | const event = await EventModel.findById(eventId); 77 | event.capacity += 1; 78 | await event.save(); 79 | 80 | return { message: 'Registration canceled successfully' }; 81 | } 82 | } 83 | 84 | module.exports = new EventService(); 85 | -------------------------------------------------------------------------------- /src/backend/services/fraudService.js: -------------------------------------------------------------------------------- 1 | // fraudService.js 2 | 3 | const FraudModel = require('../models/fraudModel'); 4 | const TransactionModel = require('../models/transactionModel'); 5 | const UserModel = require('../models/userModel'); 6 | 7 | class FraudService { 8 | // Log a fraud report 9 | async logFraudReport(userId, transactionId, reason) { 10 | const transaction = await TransactionModel.findById(transactionId); 11 | if (!transaction) { 12 | throw new Error('Transaction not found'); 13 | } 14 | 15 | const fraudReport = new FraudModel({ 16 | userId, 17 | transactionId, 18 | reportedBy: userId, 19 | reason, 20 | status: 'pending', 21 | }); 22 | 23 | await fraudReport.save(); 24 | return fraudReport; 25 | } 26 | 27 | // Analyze recent transactions for potential fraud 28 | async analyzeTransactions() { 29 | const transactions = await TransactionModel.find().sort({ createdAt: -1 }).limit(100); // Get the last 100 transactions 30 | const fraudAlerts = []; 31 | 32 | for (const transaction of transactions) { 33 | const isFraudulent = this.isTransactionFraudulent(transaction); 34 | if (isFraudulent) { 35 | fraudAlerts.push(transaction); 36 | await this.createFraudAlert(transaction); // Create a fraud alert if detected 37 | } 38 | } 39 | 40 | return fraudAlerts; 41 | } 42 | 43 | // Check if a transaction is fraudulent (placeholder logic) 44 | isTransactionFraudulent(transaction) { 45 | // Implement your fraud detection logic here (e.g., anomaly detection, rule-based checks) 46 | // Example: Check if the transaction amount exceeds a certain threshold 47 | return transaction.amount > 10000; // Placeholder condition 48 | } 49 | 50 | // Create a fraud alert 51 | async createFraudAlert(transaction) { 52 | const fraudAlert = new FraudModel({ 53 | transactionId: transaction._id, 54 | userId: transaction.userId, 55 | reason: 'Suspicious transaction amount', 56 | status: 'investigating', 57 | }); 58 | 59 | await fraudAlert.save(); 60 | } 61 | 62 | // Get fraud reports by user 63 | async getFraudReportsByUser (userId) { 64 | return await FraudModel.find({ userId }).populate('transactionId').populate('reportedBy', 'username'); 65 | } 66 | 67 | // Take action on a fraud alert 68 | async takeActionOnFraudAlert(alertId, action) { 69 | const fraudAlert = await FraudModel.findById(alertId); 70 | if (!fraudAlert) { 71 | throw new Error('Fraud alert not found'); 72 | } 73 | 74 | // Update the status based on the action taken 75 | if (action === 'investigate') { 76 | fraudAlert.status = 'investigating'; 77 | } else if (action === 'dismiss') { 78 | fraudAlert.status = 'dismissed'; 79 | } else if (action === 'resolve') { 80 | fraudAlert.status = 'resolved'; 81 | } 82 | 83 | await fraudAlert.save(); 84 | return fraudAlert; 85 | } 86 | 87 | // Get all fraud alerts (admin only) 88 | async getAllFraudAlerts() { 89 | return await FraudModel.find().populate('transactionId').populate('reportedBy', 'username'); 90 | } 91 | } 92 | 93 | module.exports = new FraudService(); 94 | -------------------------------------------------------------------------------- /src/backend/services/notificationService.js: -------------------------------------------------------------------------------- 1 | // src/backend/services/notificationService.js 2 | const Notification = require('../models/notificationModel'); 3 | 4 | // Send a notification 5 | const sendNotification = async (userId, message) => { 6 | // Validate input 7 | if (!userId || !message) { 8 | throw new Error('User ID and message are required.'); 9 | } 10 | 11 | // Create a new notification 12 | const notification = new Notification({ 13 | userId, 14 | message, 15 | }); 16 | 17 | // Save the notification to the database 18 | await notification.save(); 19 | 20 | return notification; 21 | }; 22 | 23 | // Get notifications for a specific user 24 | const getUser Notifications = async (userId) => { 25 | // Validate input 26 | if (!userId) { 27 | throw new Error('User ID is required.'); 28 | } 29 | 30 | // Retrieve notifications for the user 31 | const notifications = await Notification.find({ userId }).sort({ createdAt: -1 }); 32 | return notifications; 33 | }; 34 | 35 | // Mark a notification as read 36 | const markNotificationAsRead = async (notificationId) => { 37 | // Validate input 38 | if (!notificationId) { 39 | throw new Error('Notification ID is required.'); 40 | } 41 | 42 | // Find the notification and mark it as read 43 | const notification = await Notification.findById(notificationId); 44 | if (!notification) { 45 | throw new Error('Notification not found.'); 46 | } 47 | 48 | notification.markAsRead(); // Call the method defined in the model 49 | return notification; 50 | }; 51 | 52 | // Delete a notification 53 | const deleteNotification = async (notificationId) => { 54 | // Validate input 55 | if (!notificationId) { 56 | throw new Error('Notification ID is required.'); 57 | } 58 | 59 | // Delete the notification 60 | const notification = await Notification.findByIdAndDelete(notificationId); 61 | if (!notification) { 62 | throw new Error('Notification not found.'); 63 | } 64 | 65 | return notification; 66 | }; 67 | 68 | // Export the notification service functions 69 | module.exports = { 70 | sendNotification, 71 | getUser Notifications, 72 | markNotificationAsRead, 73 | deleteNotification, 74 | }; 75 | -------------------------------------------------------------------------------- /src/backend/services/paymentService.js: -------------------------------------------------------------------------------- 1 | // src/backend/services/paymentService.js 2 | const Transaction = require('../models/transactionModel'); 3 | const User = require('../models/userModel'); 4 | const Charity = require('../models/charityModel'); 5 | 6 | // Process a payment 7 | const processPayment = async (userId, amount, charityId) => { 8 | // Validate input 9 | if (!userId || !amount || !charityId) { 10 | throw new Error('User ID, amount, and charity ID are required.'); 11 | } 12 | 13 | // Check if user exists 14 | const user = await User.findById(userId); 15 | if (!user) { 16 | throw new Error('User not found.'); 17 | } 18 | 19 | // Check if charity exists 20 | const charity = await Charity.findById(charityId); 21 | if (!charity) { 22 | throw new Error('Charity not found.'); 23 | } 24 | 25 | // Create a new transaction 26 | const transaction = new Transaction({ 27 | userId, 28 | amount, 29 | status: 'completed', // Assuming the payment is successful 30 | description: `Donation to ${charity.name}` 31 | }); 32 | 33 | // Save the transaction 34 | await transaction.save(); 35 | 36 | // Update the charity's total donations 37 | await charity.updateTotalDonations(amount); 38 | 39 | return transaction; 40 | }; 41 | 42 | // Get payment history for a user 43 | const getPaymentHistory = async (userId) => { 44 | // Validate input 45 | if (!userId) { 46 | throw new Error('User ID is required.'); 47 | } 48 | 49 | // Retrieve payment history for the user 50 | const payments = await Transaction.find({ userId }).sort({ createdAt: -1 }); 51 | return payments; 52 | }; 53 | 54 | // Export the payment service functions 55 | module.exports = { 56 | processPayment, 57 | getPaymentHistory 58 | }; 59 | -------------------------------------------------------------------------------- /src/backend/services/userService.js: -------------------------------------------------------------------------------- 1 | // src/backend/services/userService.js 2 | const User = require('../models/userModel'); 3 | const bcrypt = require('bcrypt'); 4 | const jwt = require('jsonwebtoken'); 5 | 6 | // Register a new user 7 | const registerUser = async (username, password, email) => { 8 | // Validate input 9 | if (!username || !password || !email) { 10 | throw new Error('Username, password, and email are required.'); 11 | } 12 | 13 | // Check if the username or email already exists 14 | const existingUser = await User.findOne({ $or: [{ username }, { email }] }); 15 | if (existingUser ) { 16 | throw new Error('Username or email already exists.'); 17 | } 18 | 19 | // Hash the password 20 | const hashedPassword = await bcrypt.hash(password, 10); 21 | 22 | // Create a new user 23 | const newUser = new User({ 24 | username, 25 | password: hashedPassword, 26 | email, 27 | }); 28 | 29 | // Save the user to the database 30 | await newUser .save(); 31 | 32 | return newUser ; 33 | }; 34 | 35 | // Log in a user 36 | const loginUser = async (username, password) => { 37 | // Validate input 38 | if (!username || !password) { 39 | throw new Error('Username and password are required.'); 40 | } 41 | 42 | // Find the user by username 43 | const user = await User.findOne({ username }); 44 | if (!user) { 45 | throw new Error('Invalid username or password.'); 46 | } 47 | 48 | // Compare the provided password with the stored hashed password 49 | const isMatch = await bcrypt.compare(password, user.password); 50 | if (!isMatch) { 51 | throw new Error('Invalid username or password.'); 52 | } 53 | 54 | // Generate a JWT token 55 | const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); 56 | 57 | return { user, token }; 58 | }; 59 | 60 | // Get user information by user ID 61 | const getUser Info = async (userId) => { 62 | // Validate input 63 | if (!userId) { 64 | throw new Error('User ID is required.'); 65 | } 66 | 67 | // Retrieve the user by ID, excluding the password 68 | const user = await User.findById(userId).select('-password'); 69 | if (!user) { 70 | throw new Error('User not found.'); 71 | } 72 | 73 | return user; 74 | }; 75 | 76 | // Export the user service functions 77 | module.exports = { 78 | registerUser , 79 | loginUser , 80 | getUser Info, 81 | }; 82 | -------------------------------------------------------------------------------- /src/backend/utils/emailService.js: -------------------------------------------------------------------------------- 1 | // src/backend/utils/emailService.js 2 | const nodemailer = require('nodemailer'); 3 | 4 | // Create a transporter for sending emails 5 | const transporter = nodemailer.createTransport({ 6 | host: process.env.EMAIL_HOST, // e.g., 'smtp.example.com' 7 | port: process.env.EMAIL_PORT, // e.g., 587 8 | secure: process.env.EMAIL_SECURE === 'true', // true for 465, false for other ports 9 | auth: { 10 | user: process.env.EMAIL_USER, // Your email address 11 | pass: process.env.EMAIL_PASS, // Your email password or app-specific password 12 | }, 13 | }); 14 | 15 | // Function to send an email 16 | const sendEmail = async (to, subject, text, html) => { 17 | // Validate input 18 | if (!to || !subject || !text) { 19 | throw new Error('Recipient, subject, and text are required to send an email.'); 20 | } 21 | 22 | const mailOptions = { 23 | from: process.env.EMAIL_FROM, // Sender address 24 | to, // List of recipients 25 | subject, // Subject line 26 | text, // Plain text body 27 | html, // HTML body (optional) 28 | }; 29 | 30 | try { 31 | const info = await transporter.sendMail(mailOptions); 32 | console.log('Email sent:', info.response); 33 | return info; 34 | } catch (error) { 35 | console.error('Error sending email:', error); 36 | throw new Error('Failed to send email.'); 37 | } 38 | }; 39 | 40 | // Export the email sending function 41 | module.exports = { 42 | sendEmail, 43 | }; 44 | -------------------------------------------------------------------------------- /src/backend/utils/encryptionService.js: -------------------------------------------------------------------------------- 1 | // encryptionService.js 2 | 3 | const crypto = require('crypto'); 4 | 5 | // Define constants for encryption 6 | const ALGORITHM = 'aes-256-cbc'; // Encryption algorithm 7 | const SECRET_KEY = process.env.SECRET_KEY || 'your-secret-key'; // Secret key for encryption (should be stored securely) 8 | const IV_LENGTH = 16; // Initialization vector length 9 | 10 | // Function to encrypt data 11 | const encrypt = (text) => { 12 | // Generate a random initialization vector 13 | const iv = crypto.randomBytes(IV_LENGTH); 14 | const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(SECRET_KEY, 'hex'), iv); 15 | let encrypted = cipher.update(text, 'utf8', 'hex'); 16 | encrypted += cipher.final('hex'); 17 | 18 | // Return the IV and encrypted data as a single string 19 | return iv.toString('hex') + ':' + encrypted; 20 | }; 21 | 22 | // Function to decrypt data 23 | const decrypt = (encryptedText) => { 24 | // Split the IV and encrypted data 25 | const parts = encryptedText.split(':'); 26 | const iv = Buffer.from(parts.shift(), 'hex'); 27 | const encryptedData = Buffer.from(parts.join(':'), 'hex'); 28 | const decipher = crypto.createDecipheriv(ALGORITHM, Buffer.from(SECRET_KEY, 'hex'), iv); 29 | let decrypted = decipher.update(encryptedData, 'hex', 'utf8'); 30 | decrypted += decipher.final('utf8'); 31 | return decrypted; 32 | }; 33 | 34 | // Function to hash passwords 35 | const hashPassword = (password) => { 36 | return crypto.createHash('sha256').update(password).digest('hex'); 37 | }; 38 | 39 | // Function to compare hashed passwords 40 | const comparePasswords = (password, hashedPassword) => { 41 | const hashedInputPassword = hashPassword(password); 42 | return hashedInputPassword === hashedPassword; 43 | }; 44 | 45 | module.exports = { 46 | encrypt, 47 | decrypt, 48 | hashPassword, 49 | comparePasswords, 50 | }; 51 | -------------------------------------------------------------------------------- /src/backend/utils/logger.js: -------------------------------------------------------------------------------- 1 | // src/backend/utils/logger.js 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | // Define the log file path 6 | const logFilePath = path.join(__dirname, 'application.log'); 7 | 8 | // Function to log messages 9 | const logMessage = (message) => { 10 | const timestamp = new Date().toISOString(); 11 | const logEntry = `${timestamp} - ${message}\n`; 12 | 13 | // Append the log entry to the log file 14 | fs.appendFile(logFilePath, logEntry, (err) => { 15 | if (err) { 16 | console.error('Failed to write to log file:', err); 17 | } 18 | }); 19 | }; 20 | 21 | // Function to log errors 22 | const logError = (error) => { 23 | const timestamp = new Date().toISOString(); 24 | const logEntry = `${timestamp} - ERROR: ${error}\n`; 25 | 26 | // Append the error entry to the log file 27 | fs.appendFile(logFilePath, logEntry, (err) => { 28 | if (err) { 29 | console.error('Failed to write to log file:', err); 30 | } 31 | }); 32 | }; 33 | 34 | // Export the logging functions 35 | module.exports = { 36 | logMessage, 37 | logError, 38 | }; 39 | -------------------------------------------------------------------------------- /src/backend/utils/notificationUtils.js: -------------------------------------------------------------------------------- 1 | // notificationUtils.js 2 | 3 | const nodemailer = require('nodemailer'); // For sending email notifications 4 | const { UserModel } = require('../models/userModel'); // Assuming a User model exists 5 | 6 | // Function to create a notification message 7 | const createNotificationMessage = (type, data) => { 8 | switch (type) { 9 | case 'event_registration': 10 | return `You have successfully registered for the event: ${data.eventTitle}.`; 11 | case 'event_reminder': 12 | return `Reminder: The event "${data.eventTitle}" is happening on ${data.eventDate}.`; 13 | case 'fraud_alert': 14 | return `Alert: Suspicious activity detected on your account. Please check your recent transactions.`; 15 | case 'community_post': 16 | return `New post in the community: "${data.postTitle}" by ${data.username}.`; 17 | default: 18 | return 'You have a new notification.'; 19 | } 20 | }; 21 | 22 | // Function to send email notifications 23 | const sendEmailNotification = async (to, subject, message) => { 24 | const transporter = nodemailer.createTransport({ 25 | service: 'Gmail', // Use your email service 26 | auth: { 27 | user: process.env.EMAIL_USER, // Your email address 28 | pass: process.env.EMAIL_PASS, // Your email password 29 | }, 30 | }); 31 | 32 | const mailOptions = { 33 | from: process.env.EMAIL_USER, 34 | to, 35 | subject, 36 | text: message, 37 | }; 38 | 39 | try { 40 | await transporter.sendMail(mailOptions); 41 | console.log('Email sent successfully to:', to); 42 | } catch (error) { 43 | console.error('Error sending email:', error); 44 | } 45 | }; 46 | 47 | // Function to send push notifications (placeholder for actual implementation) 48 | const sendPushNotification = (userId, title, message) => { 49 | // Placeholder for push notification logic 50 | console.log(`Push notification sent to user ${userId}: ${title} - ${message}`); 51 | }; 52 | 53 | // Function to log notifications to the database 54 | const logNotificationToDatabase = async (userId, type, message) => { 55 | const notification = new NotificationModel({ 56 | userId, 57 | type, 58 | message, 59 | createdAt: new Date(), 60 | }); 61 | 62 | await notification.save(); 63 | }; 64 | 65 | // Function to get user notification preferences 66 | const getUser NotificationPreferences = async (userId) => { 67 | const user = await UserModel.findById(userId); 68 | return user.notificationPreferences; // Assuming preferences are stored in the User model 69 | }; 70 | 71 | // Function to send notifications based on user preferences 72 | const notifyUser = async (userId, type, data) => { 73 | const preferences = await getUser NotificationPreferences(userId); 74 | const message = createNotificationMessage(type, data); 75 | 76 | if (preferences.email) { 77 | const user = await UserModel.findById(userId); 78 | await sendEmailNotification(user.email, 'New Notification', message); 79 | } 80 | 81 | if (preferences.push) { 82 | sendPushNotification(userId, 'New Notification', message); 83 | } 84 | 85 | // Log the notification to the database 86 | await logNotificationToDatabase(userId, type, message); 87 | }; 88 | 89 | module.exports = { 90 | createNotificationMessage, 91 | sendEmailNotification, 92 | sendPushNotification, 93 | logNotificationToDatabase, 94 | getUser NotificationPreferences, 95 | notifyUser , 96 | }; 97 | -------------------------------------------------------------------------------- /src/backend/utils/smsService.js: -------------------------------------------------------------------------------- 1 | // src/backend/utils/smsService.js 2 | const twilio = require('twilio'); 3 | 4 | // Initialize Twilio client 5 | const client = new twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); 6 | 7 | // Function to send an SMS 8 | const sendSMS = async (to, message) => { 9 | // Validate input 10 | if (!to || !message) { 11 | throw new Error('Recipient phone number and message are required to send an SMS.'); 12 | } 13 | 14 | try { 15 | const sms = await client.messages.create({ 16 | body: message, 17 | from: process.env.TWILIO_PHONE_NUMBER, // Your Twilio phone number 18 | to, // Recipient's phone number 19 | }); 20 | 21 | console.log('SMS sent:', sms.sid); 22 | return sms; 23 | } catch (error) { 24 | console.error('Error sending SMS:', error); 25 | throw new Error('Failed to send SMS.'); 26 | } 27 | }; 28 | 29 | // Export the SMS sending function 30 | module.exports = { 31 | sendSMS, 32 | }; 33 | -------------------------------------------------------------------------------- /src/backend/utils/validationService.js: -------------------------------------------------------------------------------- 1 | // validationService.js 2 | 3 | const { body, param, validationResult } = require('express-validator'); 4 | 5 | // Function to validate user registration input 6 | const validateUser Registration = () => { 7 | return [ 8 | body('username') 9 | .trim() 10 | .notEmpty() 11 | .withMessage('Username is required.') 12 | .isLength({ min: 3, max: 30 }) 13 | .withMessage('Username must be between 3 and 30 characters long.'), 14 | body('email') 15 | .trim() 16 | .notEmpty() 17 | .withMessage('Email is required.') 18 | .isEmail() 19 | .withMessage('Invalid email format.'), 20 | body('password') 21 | .notEmpty() 22 | .withMessage('Password is required.') 23 | .isLength({ min: 6 }) 24 | .withMessage('Password must be at least 6 characters long.'), 25 | ]; 26 | }; 27 | 28 | // Function to validate user login input 29 | const validateUser Login = () => { 30 | return [ 31 | body('email') 32 | .trim() 33 | .notEmpty() 34 | .withMessage('Email is required.') 35 | .isEmail() 36 | .withMessage('Invalid email format.'), 37 | body('password') 38 | .notEmpty() 39 | .withMessage('Password is required.'), 40 | ]; 41 | }; 42 | 43 | // Function to validate event creation input 44 | const validateEventInput = () => { 45 | return [ 46 | body('title') 47 | .trim() 48 | .notEmpty() 49 | .withMessage('Event title is required.') 50 | .isLength({ max: 100 }) 51 | .withMessage('Event title must be at most 100 characters long.'), 52 | body('description') 53 | .trim() 54 | .notEmpty() 55 | .withMessage('Event description is required.') 56 | .isLength({ max: 2000 }) 57 | .withMessage('Event description must be at most 2000 characters long.'), 58 | body('date') 59 | .isISO8601() 60 | .withMessage('Event date must be a valid date.'), 61 | body('location') 62 | .trim() 63 | .notEmpty() 64 | .withMessage('Event location is required.'), 65 | body('capacity') 66 | .isInt({ min: 1 }) 67 | .withMessage('Event capacity must be a positive integer.'), 68 | ]; 69 | }; 70 | 71 | // Function to validate registration for an event 72 | const validateRegistrationInput = () => { 73 | return [ 74 | body('eventId') 75 | .trim() 76 | .notEmpty() 77 | .withMessage('Event ID is required.'), 78 | ]; 79 | }; 80 | 81 | // Function to validate fraud report input 82 | const validateFraudReportInput = () => { 83 | return [ 84 | body('transactionId') 85 | .trim() 86 | .notEmpty() 87 | .withMessage('Transaction ID is required.'), 88 | body('reason') 89 | .trim() 90 | .notEmpty() 91 | .withMessage('Reason for reporting fraud is required.'), 92 | ]; 93 | }; 94 | 95 | // Function to validate input and return errors 96 | const validateInput = (req, res, next) => { 97 | const errors = validationResult(req); 98 | if (!errors.isEmpty()) { 99 | return res.status(400).json({ success: false, errors: errors.array() }); 100 | } 101 | next(); 102 | }; 103 | 104 | module.exports = { 105 | validateUser Registration, 106 | validateUser Login, 107 | validateEventInput, 108 | validateRegistrationInput, 109 | validateFraudReportInput, 110 | validateInput, 111 | }; 112 | -------------------------------------------------------------------------------- /src/frontend/assets/PiConnet_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KOSASIH/PiConnect-Ecosystem/66a132fa9f8714f3e421cafeec560a557782abc5/src/frontend/assets/PiConnet_logo.png -------------------------------------------------------------------------------- /src/frontend/assets/icons/icons.png: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/frontend/components/AiRecommendations.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import axios from 'axios'; 3 | import './AiRecommendations.css'; // Import CSS for styling 4 | 5 | const AiRecommendations = () => { 6 | const [recommendations, setRecommendations] = useState([]); 7 | const [loading, setLoading] = useState(true); 8 | const [error, setError] = useState(null); 9 | 10 | // Fetch AI recommendations from the backend 11 | const fetchRecommendations = async () => { 12 | try { 13 | const response = await axios.get('/api/recommendations'); // Adjust the API endpoint as needed 14 | setRecommendations(response.data); 15 | } catch (err) { 16 | setError('Failed to fetch recommendations. Please try again later.'); 17 | } finally { 18 | setLoading(false); 19 | } 20 | }; 21 | 22 | useEffect(() => { 23 | fetchRecommendations(); 24 | }, []); 25 | 26 | if (loading) { 27 | return
{recommendation.description}
42 | View More 43 |No posts available.
62 | ) : ( 63 |{post.content}
68 | 69 |{comment.content}
75 |{event.description}
15 |Date: {new Date(event.date).toLocaleString()}
16 |Location: {event.location}
17 |Capacity: {event.capacity} spots available
18 | 21 |No fraud alerts at this time.
39 | ) : ( 40 |{alert.message}
45 |Status: {alert.status}
46 |Date: {new Date(alert.createdAt).toLocaleString()}
47 |Empowering Charitable Giving with Technology
11 | 38 |Date | 46 |Amount | 47 |Status | 48 |
---|---|---|
{new Date(transaction.date).toLocaleDateString()} | 55 |${transaction.amount.toFixed(2)} | 56 |{transaction.status} | 57 |
No transactions found. | 62 |
Empowering Charitable Giving with Technology
36 | Explore Charities 37 |${charity.description}
29 | 30 | `; 31 | charityList.appendChild(charityCard); 32 | }); 33 | }; 34 | 35 | // Function to handle donations 36 | const donate = async (charityId) => { 37 | const amount = prompt('Enter donation amount:'); 38 | if (amount && !isNaN(amount) && amount > 0) { 39 | try { 40 | const response = await fetch(`/api/charities/${charityId}/donate`, { 41 | method: 'POST', 42 | headers: { 43 | 'Content-Type': 'application/json', 44 | }, 45 | body: JSON.stringify({ amount }), 46 | }); 47 | if (!response.ok) { 48 | throw new Error('Network response was not ok'); 49 | } 50 | const result = await response.json(); 51 | alert(`Thank you for your donation of $${amount} to ${result.charityName}!`); 52 | } catch (error) { 53 | console.error('Error processing donation:', error); 54 | displayError('Failed to process donation. Please try again later.'); 55 | } 56 | } else { 57 | alert('Please enter a valid donation amount.'); 58 | } 59 | }; 60 | 61 | // Function to display error messages 62 | const displayError = (message) => { 63 | const charityList = document.getElementById('charity-list'); 64 | charityList.innerHTML = `