├── LICENSE ├── README.md ├── client-heroku ├── .gitignore ├── client │ ├── .env │ ├── package.json │ ├── public │ │ ├── index.html │ │ ├── logo.ico │ │ ├── logo.png │ │ ├── logo192.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.js │ │ ├── abis │ │ │ └── .txt │ │ ├── assests │ │ │ ├── auth.svg │ │ │ ├── forget.svg │ │ │ ├── login.svg │ │ │ ├── loginSADG.svg │ │ │ ├── logo.svg │ │ │ ├── reset.svg │ │ │ ├── update.svg │ │ │ └── welcome.svg │ │ ├── index.js │ │ └── screens │ │ │ └── Certificate.jsx │ └── truffle-config.js ├── config │ └── config.env ├── models │ └── certauth.model.js ├── package.json └── server.js ├── client ├── .env ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js ├── package.json ├── public │ ├── index.html │ ├── logo.ico │ ├── logo.png │ ├── logo192.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.js │ ├── Routes │ │ ├── AdminRoute.jsx │ │ └── PrivateRoute.jsx │ ├── assests │ │ ├── auth.svg │ │ ├── forget.svg │ │ ├── login-SADG.svg │ │ ├── login.svg │ │ ├── logo.svg │ │ ├── reset.svg │ │ ├── update.svg │ │ └── welcome.svg │ ├── contracts │ │ ├── Certification.sol │ │ └── Migrations.sol │ ├── helpers │ │ └── auth.js │ ├── index.js │ └── screens │ │ ├── Activate.jsx │ │ ├── Admin.jsx │ │ ├── Certificate.jsx │ │ ├── ForgetPassword.jsx │ │ ├── Login.jsx │ │ ├── Private.jsx │ │ ├── PrivateProfile.jsx │ │ ├── Register.jsx │ │ └── ResetPassword.jsx ├── test │ └── Certification.js └── truffle-config.js ├── config └── config.env ├── controllers ├── auth.controller.js └── user.controller.js ├── helpers ├── dbErrorHandling.js └── valid.js ├── models ├── auth.model.js └── certauth.model.js ├── package.json ├── routes ├── auth.route.js └── user.route.js └── server.js /README.md: -------------------------------------------------------------------------------- 1 | 2 | `OUTDATED REPO: CREATE PR IF YOU WISH TO CONTRIBUTE` :shipit: 3 | 4 | 5 | # Blockchain-based-Certificate-Authentication-System 6 | Decentralized Application to store and verify the authenticity of Academic Certificates 7 | 8 | > Built using Ethereum on local blockchain setup and deployed on Ropsten test network. 9 | 10 | | Contract deployed at | 0xAC677Fd653576A70b9fAde4396caEE4AE21fc95a | 11 | | -------------------- | ------------------------------------------ | 12 | | RPC Network | Ropsten Test Network | 13 | 14 | ## Steps to set up local development environment 15 | 16 | ### Setting local blockchain 17 | 18 | 1. We need to install Node, Ganache and Truffle. 19 | 20 | Download [Node.js](https://nodejs.org/en/download/) 21 | 22 | Download [Ganache](https://www.trufflesuite.com/ganache) & create a workspace. 23 | 24 | For Truffle 25 | ```bash 26 | npm i -g truffle 27 | ``` 28 | 29 | 1. Install dependencies 30 | 31 | Launch cmd in project folder path 32 | ```bash 33 | npm install 34 | ``` 35 | after completion 36 | ```bash 37 | cd client 38 | npm install 39 | ``` 40 | 41 | 1. Deploy the smart contract to the local blockchain. 42 | 43 | ```bash 44 | truffle migrate 45 | ``` 46 | 47 | > If the contracts are modified, then they should be re-migrated. 48 | 49 | ### Setting MongoDB 50 | 51 | 1. Create account in [MongoDB Atlas](https://www.mongodb.com/) 52 | 53 | 1. Create a cluster and get the URI key, add it to .env file. 54 | 55 | ### Setting Mail Service 56 | 57 | 1. Create an account in [Sendgrid](https://signup.sendgrid.com/) 58 | 59 | 1. Verify single sender email and get the API key, add them to .env file 60 | 61 | ### Now we can start the server 62 | 63 | > In project folder, for server 64 | ```bash 65 | npm run dev 66 | ``` 67 | > In client folder 68 | ```bash 69 | npm start 70 | ``` 71 | 72 | ## Deploying Smart Contract 73 | 74 | The contract can be deployed in any test networks. We are using Ropsten test network with the help of truffle. 75 | 76 | 1. First of all we need to have a metamask account. When we create an account in metamask a _mnemonic_ is given to us. [You can read how to get a mnemonic here.](https://support.dex.top/hc/en-us/articles/360004125614-How-to-Create-Mnemonic-Phrase-with-MetaMask-) 77 | 78 | 1. After that create a project in [Infura](https://infura.io). This will help us to use ropsten network through infura. 79 | 80 | 1. You will get an endpoint like this `https://ropsten.infura.io/yourapikey`. 81 | 82 | 1. Add this API key, Ropsten Account address and it's private key in .env files. 83 | 84 | 1. Now you can deploy the smart contract using a single command: 85 | 86 | ```BASH 87 | truffle migrate 88 | ``` 89 | ### Deploy client to heroku 90 | 91 | [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) 92 | 93 | Install [heroku-cli](https://devcenter.heroku.com/articles/heroku-cli#download-and-install) & follow the [steps](https://devcenter.heroku.com/articles/deploying-nodejs#deploy-your-application-to-heroku) to deploy. 94 | 95 | ## Youtube project demo video 96 | 97 | [![youtube-picture](https://img.youtube.com/vi/qj-LdkTO9Ic/maxresdefault.jpg)](https://youtu.be/qj-LdkTO9Ic) 98 | 99 | 100 | ### Client side Heroku 101 | 102 | Certificate Id : b2a90ded903d7b07b47c1bec2b398fd788deef5f 103 | 104 | [Click here](https://sadg-university.herokuapp.com/) to view and verify certificate. 105 | 106 | ## Contributors 107 | 108 | 109 | 110 | | ![st](https://github.com/arun664.png?size=50)
[Arun Kumar M](https://github.com/arun664) | ![st](https://github.com/vummanenidilip.png?size=50)
[Dilip Vummaneni](https://github.com/vummanenidilip) | ![st](https://github.com/gnanendraprasad.png?size=50)
[Gnanendra Prasad T](https://github.com/gnanendraprasad) | ![st](https://github.com/shashi9690.png?size=50)
[Shashidhara N](https://github.com/shashi9690)| 111 | | :---: | :---: | :---: | :---: | 112 | 113 | 114 | Happy coding!! :sunglasses: 115 | -------------------------------------------------------------------------------- /client-heroku/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /client-heroku/client/.env: -------------------------------------------------------------------------------- 1 | ROPSTEN_PRIVATE_KEY= //Private key of ropsten account 2 | -------------------------------------------------------------------------------- /client-heroku/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eth-marketplace", 3 | "version": "0.1.0", 4 | "private": true, 5 | "description": "Project 2020", 6 | "author": "SADG University", 7 | "dependencies": { 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.5.0", 10 | "@testing-library/user-event": "^7.2.1", 11 | "@truffle/hdwallet-provider": "^1.0.35", 12 | "axios": "^0.19.2", 13 | "babel-polyfill": "6.26.0", 14 | "babel-preset-env": "1.7.0", 15 | "babel-preset-es2015": "6.24.1", 16 | "babel-preset-stage-2": "6.24.1", 17 | "babel-preset-stage-3": "6.24.1", 18 | "babel-register": "6.26.0", 19 | "bootstrap": "4.3.1", 20 | "react": "^16.13.1", 21 | "react-bootstrap": "1.0.0-beta.5", 22 | "react-confirm-alert": "^2.6.1", 23 | "react-dom": "^16.13.1", 24 | "react-loading": "^2.0.3", 25 | "react-router-dom": "^5.1.2", 26 | "react-scripts": "3.4.1", 27 | "react-toastify": "^5.5.0", 28 | "truffle": "^5.0.5", 29 | "web3": "^1.0.0-beta.26", 30 | "yarn": "^1.22.4" 31 | }, 32 | "scripts": { 33 | "start": "react-scripts start", 34 | "build": "react-scripts build", 35 | "test": "react-scripts test", 36 | "eject": "react-scripts eject" 37 | }, 38 | "eslintConfig": { 39 | "extends": "react-app" 40 | }, 41 | "browserslist": [ 42 | ">0.2%", 43 | "not dead", 44 | "not ie <= 11", 45 | "not op_mini all" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /client-heroku/client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | SADG University 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /client-heroku/client/public/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun664/Blockchain-based-Certificate-Authentication-System/cef5aa13740816822102be73db5d9b97c45ea9e0/client-heroku/client/public/logo.ico -------------------------------------------------------------------------------- /client-heroku/client/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun664/Blockchain-based-Certificate-Authentication-System/cef5aa13740816822102be73db5d9b97c45ea9e0/client-heroku/client/public/logo.png -------------------------------------------------------------------------------- /client-heroku/client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun664/Blockchain-based-Certificate-Authentication-System/cef5aa13740816822102be73db5d9b97c45ea9e0/client-heroku/client/public/logo192.png -------------------------------------------------------------------------------- /client-heroku/client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client-heroku/client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client-heroku/client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import loginSADG from '../src/assests/loginSADG.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | import { Component } from 'react'; 6 | 7 | import Certification from '../src/abis/Certification.json'; 8 | import Web3 from 'web3'; 9 | 10 | 11 | class App extends Component { 12 | constructor(props){ 13 | super(props) 14 | this.state = { 15 | certid : '' 16 | } 17 | } 18 | 19 | 20 | 21 | handleChange = e => { 22 | e.preventDefault() 23 | this.setState({ 24 | [e.target.name]:e.target.value 25 | }) 26 | } 27 | 28 | 29 | handleSubmit = e => { 30 | e.preventDefault() 31 | 32 | const data = this.state.certid 33 | 34 | //this.props.history.push('src/screens/Certificate') 35 | this.props.history.push({ 36 | pathname: '/Certificate', 37 | state: { id : data} 38 | }) 39 | 40 | } 41 | 42 | render () { 43 | const { certid } = this.state 44 | return( 45 |
46 |
47 | 48 |
49 |
50 |
51 |

52 | Enter the Certificate ID 53 |

54 | 55 |
59 |
60 | 73 |
74 |
75 | Code 76 |
77 |
78 | 79 | 80 | 81 | 82 | 83 |
84 |
85 |
86 |
87 |
88 |
92 |
93 |
94 |
95 | 98 |
99 | ) 100 | } 101 | } 102 | 103 | export default App; 104 | -------------------------------------------------------------------------------- /client-heroku/client/src/abis/.txt: -------------------------------------------------------------------------------- 1 | Add your abi files that were generated when migrated to ropsten network. -------------------------------------------------------------------------------- /client-heroku/client/src/assests/forget.svg: -------------------------------------------------------------------------------- 1 | forgot password -------------------------------------------------------------------------------- /client-heroku/client/src/assests/login.svg: -------------------------------------------------------------------------------- 1 | remotely -------------------------------------------------------------------------------- /client-heroku/client/src/assests/reset.svg: -------------------------------------------------------------------------------- 1 | mobile login -------------------------------------------------------------------------------- /client-heroku/client/src/assests/update.svg: -------------------------------------------------------------------------------- 1 | portfolio update -------------------------------------------------------------------------------- /client-heroku/client/src/assests/welcome.svg: -------------------------------------------------------------------------------- 1 | welcoming -------------------------------------------------------------------------------- /client-heroku/client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {BrowserRouter, Route, Redirect, Switch} from 'react-router-dom'; 4 | import App from './App'; 5 | import Certificate from './screens/Certificate'; 6 | import 'react-toastify/dist/ReactToastify.css'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | 12 | }/> 13 | 14 | 15 | 16 | , 17 | document.getElementById('root') 18 | ); 19 | -------------------------------------------------------------------------------- /client-heroku/client/src/screens/Certificate.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, Component } from 'react'; 2 | import { ToastContainer, toast } from 'react-toastify'; 3 | import axios from 'axios'; 4 | 5 | import Certification from '../abis/Certification.json'; 6 | import Web3 from 'web3' 7 | 8 | import ReactLoading from "react-loading"; 9 | import logo from '../assests/logo.svg' 10 | 11 | class Certificate extends Component { 12 | 13 | constructor(props){ 14 | super(props) 15 | this.state = { 16 | txid : '', 17 | verifyc: false, 18 | buttonColor: '#667eea', 19 | verifyt:'Verify', 20 | dvisi:'hidden' 21 | } 22 | } 23 | 24 | async componentWillMount(){ 25 | await this.loadData() 26 | //console.log(this.props.location.state.id) 27 | } 28 | 29 | async componentDidUpdate(){ 30 | await this.transactionX() 31 | } 32 | 33 | 34 | async loadData(){ 35 | const Rapi = await axios.get('/api/getrapi') 36 | //window.alert(Rapi.data) 37 | var web3 = new Web3(new Web3.providers.HttpProvider( 38 | `https://ropsten.infura.io/v3/${Rapi.data}` 39 | )); 40 | 41 | const RDapi = await axios.get('/api/rdefault') 42 | 43 | web3.eth.defaultAccount = `${RDapi.data}`; 44 | this.setState({account:web3.eth.defaultAccount}) 45 | 46 | const networkData = Certification.networks[3] 47 | if(networkData){ 48 | const certification = await new web3.eth.Contract(Certification.abi, networkData.address) 49 | this.setState({certification}) 50 | //console.log("Account found!" + this.state.account); 51 | //console.log(this.state.certification); 52 | } 53 | else{ 54 | window.alert('Error') 55 | } 56 | 57 | const cert = await this.state.certification.methods.getData(this.props.location.state.id).call() 58 | //console.log(cert) 59 | 60 | const usn = cert[0]; 61 | const name = cert[1]; 62 | const email = cert[2]; 63 | const fname = cert[3]; 64 | const branch = cert[4]; 65 | const sem1 = cert[5][0]/100; 66 | const sem2 = cert[5][1]/100; 67 | const sem3 = cert[5][2]/100; 68 | const sem4 = cert[5][3]/100; 69 | const sem5 = cert[5][4]/100; 70 | const sem6 = cert[5][5]/100; 71 | const sem7 = cert[5][6]/100; 72 | const sem8 = cert[5][7]/100; 73 | const bdate = cert[6].toString() 74 | 75 | //changing date format 76 | const x = bdate[0]+bdate[1]+bdate[2]+bdate[3] 77 | const y = bdate[4]+bdate[5] 78 | const z = bdate[6]+bdate[7] 79 | 80 | //calculating cgpa 81 | const cgpa = ((parseFloat(sem1)+parseFloat(sem2)+parseFloat(sem3)+parseFloat(sem4)+parseFloat(sem5)+parseFloat(sem6)+parseFloat(sem7)+parseFloat(sem8))/(8.0)).toFixed(2) 82 | 83 | //calculating percentage & rank 84 | const per = (cgpa - 0.75) * 10 85 | //console.log(per) 86 | if(per>=70) this.setState({rank:"FCD"}) 87 | else if(per>=60 && per<70) this.setState({rank:"First Class"}) 88 | else this.setState({rank:"Second Class"}) 89 | 90 | this.setState({usn, name, email, fname, branch, x, y, z, sem1, sem2, sem3, sem4, sem5, sem6, sem7, sem8, cgpa }) 91 | //console.log(this.state) 92 | 93 | //fetching transaction id from mongo 94 | const id = this.state.usn 95 | const tixd = await axios.post('/api/idfetch', { id }) 96 | this.setState({txid:tixd.data.tid}) 97 | } 98 | 99 | async transactionX () { 100 | const Rapi = await axios.get('/api/getrapi') 101 | 102 | var web3 = new Web3(new Web3.providers.HttpProvider( 103 | `https://ropsten.infura.io/v3/${Rapi.data}` 104 | )); 105 | //console.log(this.state.txid) 106 | const certget = await web3.eth.getTransactionReceipt(this.state.txid) 107 | //console.log(certget) 108 | this.setState({newcert:certget}) 109 | } 110 | 111 | handleSubmit = e => { 112 | e.preventDefault(); 113 | 114 | //Fetching transaction details 115 | // console.log(this.state.newcert) 116 | if(this.state.newcert.status == true && this.state.newcert.transactionHash == this.state.txid ) { 117 | //toast.success("Authentication Successfull!!") 118 | toast.success("Certificate is present in Blockchain") 119 | toast.info("Ropsten Blocknumber : "+this.state.newcert.blockNumber) 120 | //console.log("Ropsten Blocknumber : "+this.state.newcert.blockNumber) 121 | this.setState({verifyc:true}) 122 | this.setState({buttonColor:'#07bc0c'}) 123 | this.setState({verifyt:'Verified'}) 124 | this.setState({dvisi:'visible'}) 125 | this.setState({blockurl:`https://ropsten.etherscan.io/block/${this.state.newcert.blockNumber}`}) 126 | } 127 | else{ 128 | toast.error("Failed to Authenticate Certificate"); 129 | } 130 | 131 | } 132 | 133 | render() 134 | { 135 | { 136 | if(this.state.newcert) 137 | { 138 | return ( 139 |
140 | 141 |
142 |
143 |
144 |
145 | Logo 146 |
147 |
SADG University 148 | Blockchain Certificate Authentication System 149 |
150 |
151 |
152 |
153 |
154 | 155 | 156 |
157 |
158 |
159 |
160 | 161 | Student Name : {this.state.name} 162 | 163 |
164 |
165 |
166 | 167 | USN : {this.state.usn} 168 | 169 |
170 |
171 |
172 |
173 |
174 | 175 | Father Name : {this.state.fname} 176 | 177 |
178 |
179 | 180 | Date of Birth : {this.state.z}-{this.state.y}-{this.state.x} 181 | 182 |
183 |
184 |
185 |
186 | 187 |
188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 |
SemesterTotal SGPAObtained SGPA
110{this.state.sem1}
210{this.state.sem2}
310{this.state.sem3}
410{this.state.sem4}
510{this.state.sem5}
610{this.state.sem6}
710{this.state.sem7}
810{this.state.sem8}
239 |
240 |
Total CGPA
241 |
{this.state.cgpa}
242 |
Rank Obtained
243 |
{this.state.rank}
244 |
245 |
246 | 247 |
248 | Certifies that 249 |

{this.state.name}

250 | has been duly admitted to the degree of 251 |

BACHELOR OF ENGINEERING

252 | in Department of 253 |

{this.state.branch}

254 | for recognition of the fulfillment of requirements for the said degree. 255 |
256 | 257 | {/*
258 |
Director/Principal
259 |
Seal
260 |
*/} 261 | 262 |
263 | 264 |
265 |
266 | 267 |
268 |
269 |
270 |

271 | Verify Authenticity of Certificate in Blockchain Network 272 |

273 | 274 |
278 |
279 | 288 |
289 |
290 | 295 |
296 |
297 |
298 | Go To Home 299 |
300 |
301 | 312 | 313 |
314 |
315 |
316 |
317 | ) 318 | } 319 | else if(this.state.usn == '') { 320 | return( 321 |
322 |
323 |
324 |
325 |
326 |
327 |

Certificate Doesn't exist!!

328 |

Please check your Certificate ID

329 |
330 |
331 |
332 | 343 |
344 |
345 |
346 | ) 347 | } 348 | else { 349 | return ( 350 |
351 |
352 | 353 |
354 |
355 | ) 356 | } 357 | } 358 | } 359 | } 360 | 361 | export default Certificate; 362 | -------------------------------------------------------------------------------- /client-heroku/client/truffle-config.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('babel-polyfill'); 3 | require('dotenv').config() 4 | 5 | const HDWalletProvider = require('@truffle/hdwallet-provider') 6 | 7 | module.exports = { 8 | networks: { 9 | ropsten: { 10 | provider: function() { 11 | return new HDWalletProvider( 12 | process.env.ROPSTEN_PRIVATE_KEY, 13 | `https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}` 14 | ) 15 | }, 16 | gasPrice: 21, 17 | network_id: 3, 18 | }, 19 | development: { 20 | host: "127.0.0.1", 21 | port: 7545, 22 | network_id: "*" //Match any network 23 | } 24 | }, 25 | contracts_build_directory: './src/abis/', 26 | compilers: { 27 | solc: { 28 | optimizer: { 29 | enabled: true, 30 | runs: 200 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client-heroku/config/config.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | MONGO_URI= //MONGO_URI from MongoDB Atlas 3 | INFURA_API_KEY= //Infura Ropsten API key 4 | ROPSTEN_DEFAULT_ACCOUNT= //Ropsten Account address -------------------------------------------------------------------------------- /client-heroku/models/certauth.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require ('mongoose') 2 | 3 | const TransactionSchema = new mongoose.Schema( 4 | { 5 | usn: { 6 | type: String, 7 | trim: true 8 | }, 9 | tid: { 10 | type: String, 11 | trim: true 12 | } 13 | } 14 | ); 15 | 16 | module.exports = mongoose.model('Certificate', TransactionSchema); -------------------------------------------------------------------------------- /client-heroku/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "cd client && npm run build", 8 | "install-client": "cd client && npm install", 9 | "heroku-postbuild": "npm run install-client && npm run build", 10 | "start": "node server.js", 11 | "client": "cd client && npm start", 12 | "dev": "concurrently -n 'server,client' -c 'red,green' \"nodemon server.js\" \"npm run client\"" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "body-parser": "^1.19.0", 19 | "cors": "^2.8.5", 20 | "dotenv": "^8.2.0", 21 | "express": "^4.17.1", 22 | "express-jwt": "^5.3.3", 23 | "express-validator": "^6.4.0", 24 | "mongoose": "^5.9.9", 25 | "morgan": "^1.10.0", 26 | "node-fetch": "^2.6.0", 27 | "nodemon": "^2.0.3", 28 | "object-hash": "^2.0.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client-heroku/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const morgan = require('morgan') 3 | const bodyparser = require('body-parser') 4 | const app = express() 5 | const mongoose = require ('mongoose') 6 | const transaction = require('./models/certauth.model'); 7 | const cors = require('cors') 8 | const path = require('path') 9 | //Config .env to ./config/config.env 10 | require('dotenv').config({ 11 | path:'./config/config.env' 12 | }) 13 | 14 | 15 | //Connect to Database 16 | const uri = process.env.MONGO_URI 17 | mongoose.connect(uri, { 18 | useNewUrlParser: true, 19 | useCreateIndex: true, 20 | useUnifiedTopology: true 21 | } 22 | ); 23 | 24 | const connection = mongoose.connection; 25 | connection.once('open', () => { 26 | console.log ("MongoDB database connection established successfully"); 27 | }) 28 | 29 | 30 | //Use bodyParser 31 | app.use(bodyparser.json()) 32 | 33 | //config for only development 34 | //config for only development 35 | 36 | 37 | //Morgan give information about each request 38 | //Cors it's allow to deal with react for localhost at port 3000 without any problem 39 | app.post('/api/idfetch', (req, res, next) => { 40 | const usn = req.body.id 41 | //console.log(usn) 42 | transaction.findOne({ 43 | usn 44 | }).exec((err, tid) => { 45 | return res.send(tid) 46 | }) 47 | }); 48 | 49 | app.get('/api/getrapi', (req, res, next) => { 50 | return res.send(process.env.INFURA_API_KEY) 51 | }); 52 | 53 | app.get('/api/rdefault', (req, res, next) => { 54 | return res.send(process.env.ROPSTEN_DEFAULT_ACCOUNT) 55 | }); 56 | 57 | 58 | 59 | if (process.env.NODE_ENV === 'production') { 60 | // Serve any static files 61 | app.use(express.static(path.resolve(__dirname, 'client/build'))); 62 | // Handle React routing, return all requests to React app 63 | app.get('*', function(req, res) { 64 | res.sendFile(path.resolve(__dirname, 'client/build', 'index.html')); 65 | }); 66 | app.use(cors()); 67 | } 68 | 69 | const PORT = process.env.PORT 70 | 71 | var listener = app.listen(PORT, function() { 72 | console.log(`App listening on port ${PORT}`); //Listening on port 8888 73 | }); 74 | -------------------------------------------------------------------------------- /client/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_URL=http://localhost:5000/api 2 | ROPSTEN_PRIVATE_KEY= //Ropsten Account private key -------------------------------------------------------------------------------- /client/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /client/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const Certification = artifacts.require("Certification"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Certification); 5 | }; 6 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eth-marketplace", 3 | "version": "0.1.0", 4 | "private": true, 5 | "description": "Project SADG 2020", 6 | "author": "SADG University", 7 | "dependencies": { 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.5.0", 10 | "@testing-library/user-event": "^7.2.1", 11 | "@truffle/hdwallet-provider": "^1.0.35", 12 | "axios": "^0.19.2", 13 | "babel-polyfill": "6.26.0", 14 | "babel-preset-env": "1.7.0", 15 | "babel-preset-es2015": "6.24.1", 16 | "babel-preset-stage-2": "6.24.1", 17 | "babel-preset-stage-3": "6.24.1", 18 | "babel-register": "6.26.0", 19 | "bootstrap": "4.3.1", 20 | "chai": "4.2.0", 21 | "chai-as-promised": "7.1.1", 22 | "chai-bignumber": "3.0.0", 23 | "dotenv": "^8.2.0", 24 | "flatted": "^3.0.2", 25 | "jquery": ">=3.5.0", 26 | "js-cookie": "^2.2.1", 27 | "jsonwebtoken": "^8.5.1", 28 | "react": "^16.13.1", 29 | "react-bootstrap": "1.0.0-beta.5", 30 | "react-confirm-alert": "^2.6.1", 31 | "react-dom": "^16.13.1", 32 | "react-loading": "^2.0.3", 33 | "react-router-dom": "^5.1.2", 34 | "react-scripts": "3.4.1", 35 | "react-spinners": "^0.8.3", 36 | "react-toastify": "^5.5.0", 37 | "truffle": "^5.0.5", 38 | "web3": "^1.0.0-beta.26", 39 | "yarn": "^1.22.4" 40 | }, 41 | "scripts": { 42 | "start": "react-scripts start", 43 | "build": "react-scripts build", 44 | "test": "react-scripts test", 45 | "eject": "react-scripts eject" 46 | }, 47 | "eslintConfig": { 48 | "extends": "react-app" 49 | }, 50 | "browserslist": [ 51 | ">0.2%", 52 | "not dead", 53 | "not ie <= 11", 54 | "not op_mini all" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | SADG University 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /client/public/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun664/Blockchain-based-Certificate-Authentication-System/cef5aa13740816822102be73db5d9b97c45ea9e0/client/public/logo.ico -------------------------------------------------------------------------------- /client/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun664/Blockchain-based-Certificate-Authentication-System/cef5aa13740816822102be73db5d9b97c45ea9e0/client/public/logo.png -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arun664/Blockchain-based-Certificate-Authentication-System/cef5aa13740816822102be73db5d9b97c45ea9e0/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import Logo from './assests/logo.svg'; 5 | 6 | 7 | function App({ history }) { 8 | if ( sessionStorage.reloadAfterPageLoad ) { 9 | toast.error('Signout Successfull'); 10 | sessionStorage.reloadAfterPageLoad = false; 11 | } 12 | 13 | return ( 14 |
15 | 16 |
17 |
18 |
19 |

20 | Welcome to SADG University{' '} 21 |

22 |

23 | Blockchain Certificate Authentication System 24 |

25 |
26 |
27 |
28 | Sign up or Sign In 29 |
30 |
31 |
32 | 36 | 37 | Sign In 38 | 39 | 43 | 44 | Sign Up 45 | 46 | {/* 50 | 51 | Profile Dashbaord 52 | 53 | 57 | 58 | Admin Dashbaord 59 | */} 60 | {/* */} 72 |
73 |
74 |
75 |
76 |
77 |
81 |
82 |
83 |
84 | ) 85 | } 86 | 87 | export default App; -------------------------------------------------------------------------------- /client/src/Routes/AdminRoute.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Route, Redirect } from 'react-router-dom'; 3 | import { isAuth } from '../helpers/auth'; 4 | 5 | const AdminRoute = ({ component: Component, ...rest }) => ( 6 | 9 | isAuth() && isAuth().role === 'admin' ? ( 10 | 11 | ) : ( 12 | 18 | ) 19 | } 20 | > 21 | ); 22 | 23 | export default AdminRoute; -------------------------------------------------------------------------------- /client/src/Routes/PrivateRoute.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Route, Redirect } from 'react-router-dom'; 3 | import { isAuth } from '../helpers/auth'; 4 | 5 | const PrivateRoute = ({ component: Component, ...rest }) => ( 6 | 9 | isAuth() ? ( 10 | 11 | ) : ( 12 | 18 | ) 19 | } 20 | > 21 | ); 22 | 23 | export default PrivateRoute; -------------------------------------------------------------------------------- /client/src/assests/forget.svg: -------------------------------------------------------------------------------- 1 | forgot password -------------------------------------------------------------------------------- /client/src/assests/login.svg: -------------------------------------------------------------------------------- 1 | remotely -------------------------------------------------------------------------------- /client/src/assests/reset.svg: -------------------------------------------------------------------------------- 1 | mobile login -------------------------------------------------------------------------------- /client/src/assests/update.svg: -------------------------------------------------------------------------------- 1 | portfolio update -------------------------------------------------------------------------------- /client/src/assests/welcome.svg: -------------------------------------------------------------------------------- 1 | welcoming -------------------------------------------------------------------------------- /client/src/contracts/Certification.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | contract Certification { 3 | constructor() public {} 4 | struct Certificate { 5 | string USN; 6 | string candidate_name; 7 | string Email; 8 | string father_name; 9 | string department; 10 | uint[8] sgpa; 11 | uint256 birth_date; 12 | } 13 | mapping(bytes32 => Certificate) public certificates; 14 | event certificateGenerated(bytes32 _certificateId); 15 | 16 | function stringToBytes32(string memory source) private pure returns (bytes32 result) { 17 | bytes memory tempEmptyStringTest = bytes(source); 18 | if (tempEmptyStringTest.length == 0) { 19 | return 0x0; 20 | } 21 | assembly { 22 | result := mload(add(source, 32)) 23 | } 24 | } 25 | 26 | function generateCertificate( 27 | string memory _id, 28 | string memory _USN, 29 | string memory _candidate_name, 30 | string memory _Email, 31 | string memory _father_name, 32 | string memory _department, 33 | uint[8] memory _sgpa, 34 | uint256 _birth_date) public { 35 | bytes32 byte_id = stringToBytes32(_id); 36 | certificates[byte_id] = Certificate(_USN,_candidate_name,_Email, _father_name, _department, _sgpa, _birth_date); 37 | emit certificateGenerated(byte_id); 38 | } 39 | function getData(string memory _id) public view returns(string memory,string memory,string memory, string memory, string memory, uint[8] memory,uint256 _birth_date) { 40 | bytes32 byte_id = stringToBytes32(_id); 41 | Certificate memory temp = certificates[byte_id]; 42 | 43 | return (temp.USN, temp.candidate_name, temp.Email,temp.father_name, temp.department, temp.sgpa, temp.birth_date); 44 | } 45 | } -------------------------------------------------------------------------------- /client/src/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/src/helpers/auth.js: -------------------------------------------------------------------------------- 1 | import cookie from 'js-cookie'; 2 | 3 | // Set in Cookie 4 | export const setCookie = (key, value) => { 5 | if (window !== 'undefiend') { 6 | cookie.set(key, value, { 7 | // 1 Day 8 | expires: 1 9 | }) 10 | } 11 | } 12 | // remove from cookie 13 | export const removeCookie = key => { 14 | if (window !== 'undefined') { 15 | cookie.remove(key, { 16 | expires: 1 17 | }); 18 | } 19 | }; 20 | 21 | 22 | // Get from cookie such as stored token 23 | // Will be useful when we need to make request to server with token 24 | export const getCookie = key => { 25 | if (window !== 'undefined') { 26 | return cookie.get(key); 27 | } 28 | }; 29 | 30 | // Set in localstorage 31 | export const setLocalStorage = (key, value) => { 32 | if (window !== 'undefined') { 33 | localStorage.setItem(key, JSON.stringify(value)); 34 | } 35 | }; 36 | 37 | // Remove from localstorage 38 | export const removeLocalStorage = key => { 39 | if (window !== 'undefined') { 40 | localStorage.removeItem(key); 41 | } 42 | }; 43 | 44 | // Auth enticate user by passing data to cookie and localstorage during signin 45 | export const authenticate = (response, next) => { 46 | //console.log('AUTHENTICATE HELPER ON SIGNIN RESPONSE', response); 47 | setCookie('token', response.data.token); 48 | setLocalStorage('user', response.data.user); 49 | next(); 50 | }; 51 | 52 | // Access user info from localstorage 53 | export const isAuth = () => { 54 | if (window !== 'undefined') { 55 | const cookieChecked = getCookie('token'); 56 | if (cookieChecked) { 57 | if (localStorage.getItem('user')) { 58 | return JSON.parse(localStorage.getItem('user')); 59 | } else { 60 | return false; 61 | } 62 | } 63 | } 64 | }; 65 | 66 | //Signout 67 | export const signout = next => { 68 | removeCookie('token'); 69 | removeLocalStorage('user'); 70 | next(); 71 | }; 72 | 73 | //Update user data in localstorage 74 | export const updateUser = (response, next) => { 75 | console.log('UPDATE USER IN LOCALSTORAGE HELPERS', response); 76 | if (typeof window !== 'undefined') { 77 | let auth = JSON.parse(localStorage.getItem('user')); 78 | auth = response.data; 79 | localStorage.setItem('user', JSON.stringify(auth)); 80 | } 81 | next(); 82 | }; -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {BrowserRouter, Route, Redirect, Switch} from 'react-router-dom'; 4 | import App from './App'; 5 | import Register from './screens/Register'; 6 | import Activate from './screens/Activate'; 7 | import Login from './screens/Login'; 8 | import Private from './screens/Private.jsx'; 9 | import PrivateProfile from './screens/PrivateProfile.jsx'; 10 | import Admin from './screens/Admin.jsx'; 11 | import ForgetPassword from './screens/ForgetPassword'; 12 | import ResetPassword from './screens/ResetPassword'; 13 | import Certificate from './screens/Certificate'; 14 | 15 | import PrivateRoute from './Routes/PrivateRoute'; 16 | import AdminRoute from './Routes/AdminRoute'; 17 | import 'react-toastify/dist/ReactToastify.css'; 18 | 19 | ReactDOM.render( 20 | 21 | 22 | }/> 23 | }/> 24 | }/> 25 | }/> 26 | }/> 27 | }/> 28 | 29 | 30 | 31 | 32 | 33 | 34 | , 35 | document.getElementById('root') 36 | ); -------------------------------------------------------------------------------- /client/src/screens/Activate.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import authSvg from '../assests/welcome.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | import jwt from 'jsonwebtoken'; 6 | import { isAuth } from '../helpers/auth'; 7 | import { Redirect } from 'react-router-dom'; 8 | 9 | const Activate = ({ match }) => { 10 | const [formData, setFormData] = useState({ 11 | name: '', 12 | token: '', 13 | show: true 14 | }); 15 | 16 | useEffect(() => { 17 | /**get token from params like /active/token 18 | * then decide this token and get name 19 | */ 20 | let token = match.params.token; 21 | let { name } = jwt.decode(token); 22 | 23 | if (token) { 24 | setFormData({ ...formData, name, token }); 25 | } 26 | 27 | console.log(token, name); 28 | }, [match.params]); 29 | const { name, token, show } = formData; 30 | 31 | const handleSubmit = e => { 32 | e.preventDefault(); 33 | 34 | axios 35 | .post(`${process.env.REACT_APP_API_URL}/activation`, { 36 | token 37 | }) 38 | .then(res => { 39 | setFormData({ 40 | ...formData, 41 | show: false 42 | }); 43 | 44 | toast.success(res.data.message); 45 | }) 46 | .catch(err => { 47 | 48 | toast.error(err.response.data.errors); 49 | }); 50 | }; 51 | 52 | return ( 53 |
54 | {isAuth() ? : null} 55 | 56 |
57 |
58 |
59 |

60 | Welcome {name} 61 |

62 | 63 |
67 |
68 | 75 |
76 |
77 |
78 | Or sign up again 79 |
80 |
81 | 92 | 93 |
94 |
95 |
96 |
100 |
101 |
102 |
103 | ); 104 | }; 105 | 106 | export default Activate; 107 | -------------------------------------------------------------------------------- /client/src/screens/ForgetPassword.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import authSvg from '../assests/forget.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | 6 | const ForgetPassword = ({history}) => { 7 | const [formData, setFormData] = useState({ 8 | email: '', 9 | textChange: 'Submit' 10 | }); 11 | const { email, textChange } = formData; 12 | const handleChange = text => e => { 13 | setFormData({ ...formData, [text]: e.target.value }); 14 | }; 15 | const handleSubmit = e => { 16 | e.preventDefault(); 17 | if (email) { 18 | setFormData({ ...formData, textChange: 'Submitting' }); 19 | axios 20 | .put(`${process.env.REACT_APP_API_URL}/password/forget`, { 21 | email 22 | }) 23 | .then(res => { 24 | 25 | setFormData({ 26 | ...formData, 27 | email: '', 28 | }); 29 | toast.success(`Please check your email`); 30 | 31 | }) 32 | .catch(err => { 33 | console.log(err.response) 34 | toast.error(err.response.data.error); 35 | }); 36 | } else { 37 | toast.error('Please fill all fields'); 38 | } 39 | }; 40 | return ( 41 |
42 | 43 |
44 |
45 |
46 |

47 | Forget Password 48 |

49 |
50 | 51 |
55 | 62 | 69 | 70 |
71 |
72 |
73 |
74 |
78 |
79 |
80 |
81 | ); 82 | }; 83 | 84 | export default ForgetPassword; 85 | -------------------------------------------------------------------------------- /client/src/screens/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import authSvg from '../assests/login-SADG.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | import { authenticate, isAuth } from '../helpers/auth'; 6 | import { Link, Redirect } from 'react-router-dom'; 7 | 8 | const Login = ({ history }) => { 9 | const [formData, setFormData] = useState({ 10 | email: '', 11 | password1: '', 12 | textChange: 'Sign In' 13 | }); 14 | const { email, password1, textChange } = formData; 15 | const handleChange = text => e => { 16 | setFormData({ ...formData, [text]: e.target.value }); 17 | }; 18 | 19 | /**If not admin redirect to /private 20 | * if admin redirect to /admin 21 | */ 22 | const informParent = response => { 23 | authenticate(response, () => { 24 | isAuth() && isAuth().role === 'admin' 25 | ? history.push('/admin') 26 | : history.push('/private'); 27 | toast.success(`Hey ${response.data.user.name}, Welcome`); 28 | }); 29 | }; 30 | 31 | const handleSubmit = e => { 32 | console.log(process.env.REACT_APP_API_URL); 33 | e.preventDefault(); 34 | if (email && password1) { 35 | setFormData({ ...formData, textChange: 'Submitting' }); 36 | axios 37 | .post(`${process.env.REACT_APP_API_URL}/login`, { 38 | email, 39 | password: password1 40 | }) 41 | .then(res => { 42 | authenticate(res, () => { 43 | setFormData({ 44 | ...formData, 45 | email: '', 46 | password1: '', 47 | textChange: 'Submitted' 48 | }); 49 | isAuth() && isAuth().role === 'admin' 50 | ? history.push('/admin') 51 | : history.push('/private'); 52 | toast.success(`Hey ${res.data.user.name}, Welcome back!`); 53 | }); 54 | }) 55 | .catch(err => { 56 | setFormData({ 57 | ...formData, 58 | email: '', 59 | password1: '', 60 | textChange: 'Sign In' 61 | }); 62 | console.log(err.response); 63 | toast.error(err.response.data.errors); 64 | }); 65 | } else { 66 | toast.error('Please fill all fields'); 67 | } 68 | }; 69 | return ( 70 |
71 | {isAuth() ? : null} 72 | 73 |
74 |
75 |
76 |

77 | Sign In 78 |

79 |
80 |
84 | 91 | 98 | 105 | 109 | Forget password? 110 | 111 | 112 |
113 | 114 |
115 |
116 | Or sign In with e-mail 117 |
118 |
119 | 120 |
121 | 132 | 133 |
134 |
135 | 136 |
137 |
141 |
142 |
143 |
144 | ); 145 | }; 146 | 147 | export default Login; 148 | -------------------------------------------------------------------------------- /client/src/screens/Private.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import authSvg from '../assests/update.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | import { updateUser, isAuth, getCookie, signout } from '../helpers/auth'; 6 | import { Component } from 'react'; 7 | 8 | import Certification from '../abis/Certification.json'; 9 | import Web3 from 'web3'; 10 | 11 | 12 | class Private extends Component { 13 | constructor(props){ 14 | super(props) 15 | this.state = { 16 | certid : '' 17 | } 18 | } 19 | 20 | 21 | 22 | handleChange = e => { 23 | e.preventDefault() 24 | this.setState({ 25 | [e.target.name]:e.target.value 26 | }) 27 | } 28 | 29 | 30 | handleSubmit = e => { 31 | e.preventDefault() 32 | 33 | const data = this.state.certid 34 | 35 | this.props.history.push('/private/certificate') 36 | this.props.history.push({ 37 | pathname: '/private/certificate', 38 | state: { id : data} 39 | }) 40 | 41 | } 42 | 43 | render () { 44 | const { certid } = this.state 45 | return( 46 |
47 |
48 | 49 |
50 |
51 |
52 |

53 | Enter the Certificate ID 54 |

55 | 56 |
60 |
61 | 74 |
75 |
76 |
77 | My Profile 78 |
79 |
80 | 91 |
92 | 113 |
114 | 115 |
116 |
117 |
118 |
122 |
123 |
124 |
125 |
126 | Copyright © 2020 by SADG University. All Rights Reserved. 127 |
128 |
129 | ) 130 | } 131 | } 132 | 133 | export default Private; 134 | -------------------------------------------------------------------------------- /client/src/screens/PrivateProfile.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import authSvg from '../assests/update.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | import { updateUser, isAuth, getCookie, signout } from '../helpers/auth'; 6 | 7 | const PrivateProfile = ({ history }) => { 8 | const [formData, setFormData] = useState({ 9 | name: '', 10 | email: '', 11 | password1: '', 12 | textChange: 'Update', 13 | role: '' 14 | }); 15 | 16 | useEffect(() => { 17 | loadProfile(); 18 | }, []); 19 | 20 | const loadProfile = () => { 21 | const token = getCookie('token'); 22 | axios 23 | .get(`${process.env.REACT_APP_API_URL}/user/${isAuth()._id}`, { 24 | headers: { 25 | Authorization: `Bearer ${token}` 26 | } 27 | }) 28 | .then(res => { 29 | const { role, name, email } = res.data; 30 | setFormData({ ...formData, role, name, email }); 31 | }) 32 | .catch(err => { 33 | toast.error(`Error To Your Information ${err.response.statusText}`); 34 | if (err.response.status === 401) { 35 | signout(() => { 36 | history.push('/login'); 37 | }); 38 | } 39 | }); 40 | }; 41 | const { name, email, password1, textChange, role } = formData; 42 | const handleChange = text => e => { 43 | setFormData({ ...formData, [text]: e.target.value }); 44 | }; 45 | const handleSubmit = e => { 46 | const token = getCookie('token'); 47 | console.log(token); 48 | e.preventDefault(); 49 | setFormData({ ...formData, textChange: 'Submitting' }); 50 | axios 51 | .put( 52 | `${process.env.REACT_APP_API_URL}/user/update`, 53 | { 54 | name, 55 | email, 56 | password: password1 57 | }, 58 | { 59 | headers: { 60 | Authorization: `Bearer ${token}` 61 | } 62 | } 63 | ) 64 | .then(res => { 65 | updateUser(res, () => { 66 | toast.success('Profile Updated Successfully'); 67 | setFormData({ ...formData, textChange: 'Update' }); 68 | }); 69 | }) 70 | .catch(err => { 71 | console.log(err.response); 72 | }); 73 | }; 74 | 75 | return ( 76 |
77 | 78 |
79 |
80 |
81 |

82 | Profile Update 83 |

84 | 85 |
89 |
90 | 97 | 104 | 112 | 113 | 120 | 127 |
128 |
129 |
130 | Go To Home 131 |
132 |
133 | 144 | 145 |
146 |
147 |
148 |
152 |
153 |
154 |
155 | ); 156 | }; 157 | 158 | export default PrivateProfile; 159 | -------------------------------------------------------------------------------- /client/src/screens/Register.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import authSvg from '../assests/auth.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | import { isAuth } from '../helpers/auth'; 6 | import { Redirect } from 'react-router-dom'; 7 | 8 | const Register = () => { 9 | const [formData, setFormData] = useState({ 10 | name: '', 11 | email: '', 12 | password1: '', 13 | password2: '', 14 | textChange: 'Sign Up' 15 | }); 16 | 17 | const { name, email, password1, password2, textChange } = formData; 18 | const handleChange = text => e => { 19 | setFormData({ ...formData, [text]: e.target.value }); 20 | }; 21 | const handleSubmit = e => { 22 | e.preventDefault(); 23 | if (name && email && password1) { 24 | if (password1 === password2) { 25 | setFormData({ ...formData, textChange: 'Submitting' }); 26 | axios 27 | .post(`${process.env.REACT_APP_API_URL}/register`, { 28 | name, 29 | email, 30 | password: password1 31 | }) 32 | .then(res => { 33 | setFormData({ 34 | ...formData, 35 | name: '', 36 | email: '', 37 | password1: '', 38 | password2: '', 39 | textChange: 'Submitted' 40 | }); 41 | 42 | toast.success(res.data.message); 43 | }) 44 | .catch(err => { 45 | setFormData({ 46 | ...formData, 47 | name: '', 48 | email: '', 49 | password1: '', 50 | password2: '', 51 | textChange: 'Sign Up' 52 | }); 53 | console.log(err.response); 54 | toast.error(err.response.data.errors); 55 | }); 56 | } else { 57 | toast.error("Passwords don't matches"); 58 | } 59 | } else { 60 | toast.error('Please fill all fields'); 61 | } 62 | }; 63 | 64 | return ( 65 |
66 | {isAuth() ? : null} 67 | 68 |
69 |
70 |
71 |

72 | Sign Up 73 |

74 | 75 |
79 |
80 | 87 | 94 | 101 | 108 | 115 |
116 |
117 |
118 | Or sign with email or social login 119 |
120 |
121 | 132 | 133 |
134 |
135 |
136 |
140 |
141 |
142 |
143 | ); 144 | }; 145 | 146 | export default Register; 147 | -------------------------------------------------------------------------------- /client/src/screens/ResetPassword.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import authSvg from '../assests/reset.svg'; 3 | import { ToastContainer, toast } from 'react-toastify'; 4 | import axios from 'axios'; 5 | const ResetPassword = ({match}) => { 6 | const [formData, setFormData] = useState({ 7 | password1: '', 8 | password2: '', 9 | token: '', 10 | textChange: 'Submit' 11 | }); 12 | const { password1, password2, textChange, token } = formData; 13 | 14 | useEffect(() => { 15 | let token = match.params.token 16 | if(token) { 17 | setFormData({...formData, token,}) 18 | } 19 | 20 | }, []) 21 | const handleChange = text => e => { 22 | setFormData({ ...formData, [text]: e.target.value }); 23 | }; 24 | const handleSubmit = e => { 25 | console.log(password1, password2) 26 | e.preventDefault(); 27 | if ((password1 === password2) && password1 && password2) { 28 | setFormData({ ...formData, textChange: 'Submitting' }); 29 | axios 30 | .put(`${process.env.REACT_APP_API_URL}/password/reset`, { 31 | newPassword: password1, 32 | resetPasswordLink: token 33 | }) 34 | .then(res => { 35 | console.log(res.data.message) 36 | setFormData({ 37 | ...formData, 38 | password1: '', 39 | password2: '' 40 | }); 41 | toast.success(res.data.message); 42 | 43 | }) 44 | .catch(err => { 45 | toast.error('Something is wrong try again'); 46 | }); 47 | } else { 48 | toast.error('Passwords don\'t matches'); 49 | } 50 | }; 51 | return ( 52 |
53 | 54 |
55 |
56 |
57 |

58 | Reset Your Password 59 |

60 |
61 | 62 |
66 | 73 | 80 | 87 | 88 |
89 |
90 |
91 |
92 |
96 |
97 |
98 |
99 | ); 100 | }; 101 | 102 | export default ResetPassword; 103 | -------------------------------------------------------------------------------- /client/test/Certification.js: -------------------------------------------------------------------------------- 1 | const Certification = artifacts.require("./Certification.sol"); 2 | require('chai') 3 | .use(require('chai-as-promised')) 4 | .should() 5 | 6 | contract('Certification', () => { 7 | let certification, cert,cert1 8 | before(async () => { 9 | certification= await Certification.deployed() 10 | }) 11 | describe('deployment', async () =>{ 12 | it('deploys Succesfully', async () =>{ 13 | const address = await certification.address 14 | assert.notEqual(address, 0*0) 15 | assert.notEqual(address, '') 16 | assert.notEqual(address, null) 17 | assert.notEqual(address, undefined) 18 | })}) 19 | describe('Certification', async () =>{ 20 | it('generates a certificate', async () =>{ 21 | cert = 22 | certification.generateCertificate('test','1KG16CS096','test','test@gmail.com','test’s father','CSE',[8,8,8,8,8,8,8,8],'09/05/1999') 23 | }) 24 | }) 25 | it('returns data', async ()=>{ 26 | cert1 = certification.getData('test') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /client/truffle-config.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('babel-polyfill'); 3 | require('dotenv').config() 4 | 5 | const HDWalletProvider = require('@truffle/hdwallet-provider') 6 | 7 | module.exports = { 8 | networks: { 9 | ropsten: { 10 | provider: function() { 11 | return new HDWalletProvider( 12 | process.env.ROPSTEN_PRIVATE_KEY, 13 | `https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}` 14 | ) 15 | }, 16 | gasPrice: 21, 17 | network_id: 3, 18 | }, 19 | development: { 20 | host: "127.0.0.1", 21 | port: 7545, 22 | network_id: "*" //Match any network 23 | } 24 | }, 25 | contracts_directory: './src/contracts/', 26 | contracts_build_directory: './src/abis/', 27 | compilers: { 28 | solc: { 29 | optimizer: { 30 | enabled: true, 31 | runs: 200 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /config/config.env: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | NODE_ENV=development 3 | CLIENT_URL=http://localhost:3000 4 | MONGO_URI= //MONGO_URI link from MongoDB Atlas 5 | JWT_ACCOUNT_ACTIVATION= //Random text code 6 | JWT_SECRET= //Random text code 7 | JWT_RESET_PASSWORD= //Random text code 8 | EMAIL_FROM= //Single sender verified mail from sendgrid 9 | MAIL_KEY= //Mail Api key from sendgrid 10 | INFURA_API_KEY= //Infura Api key for Ropsten Network 11 | ROPSTEN_DEFAULT_ACCOUNT= //Ropsten Account Address from Metamask -------------------------------------------------------------------------------- /controllers/auth.controller.js: -------------------------------------------------------------------------------- 1 | const User = require('../models/auth.model'); 2 | const expressJwt = require('express-jwt'); 3 | const _ = require('lodash'); 4 | const { OAuth2Client } = require('google-auth-library'); 5 | const fetch = require('node-fetch'); 6 | 7 | const { validationResult } = require('express-validator'); 8 | const jwt = require('jsonwebtoken'); 9 | const expressJWT = require('express-jwt'); 10 | const { errorHandler } = require('../helpers/dbErrorHandling'); 11 | const sgMail = require('@sendgrid/mail'); 12 | sgMail.setApiKey(process.env.MAIL_KEY); 13 | 14 | exports.registerController = (req, res) => { 15 | const { name, email, password } = req.body; 16 | const errors = validationResult(req); 17 | 18 | if (!errors.isEmpty()) { 19 | const firstError = errors.array().map(error => error.msg)[0]; 20 | return res.status(422).json({ 21 | errors: firstError 22 | }); 23 | } else { 24 | User.findOne({ 25 | email 26 | }).exec((err, user) => { 27 | if (user) { 28 | return res.status(400).json({ 29 | errors: 'Email is taken' 30 | }); 31 | } 32 | }); 33 | 34 | const token = jwt.sign( 35 | { 36 | name, 37 | email, 38 | password 39 | }, 40 | process.env.JWT_ACCOUNT_ACTIVATION, 41 | { 42 | expiresIn: '15m' 43 | } 44 | ); 45 | const emailData = { 46 | to: email, 47 | from: process.env.EMAIL_FROM, 48 | subject: 'Account activation link', 49 | html: ` 50 |

Please use the following to activate your account

51 |

${process.env.CLIENT_URL}/users/activate/${token}

52 |
53 |

This email may containe sensitive information

54 |

${process.env.CLIENT_URL}

55 | `, 56 | }; 57 | 58 | sgMail.send(emailData).then(sent => { 59 | return res.json({ 60 | message: `Email has been sent to ${email}` 61 | }); 62 | }).catch(err => { 63 | return res.status(400).json({ 64 | success: false, 65 | errors: errorHandler(err) 66 | }); 67 | }); 68 | } 69 | }; 70 | 71 | //Register for Backend is done. 72 | 73 | 74 | // Activation and save to database 75 | 76 | exports.activationController = (req, res) => { 77 | const { token } = req.body; 78 | 79 | if (token) { 80 | //Verify the token is valid or not or expired 81 | jwt.verify(token, process.env.JWT_ACCOUNT_ACTIVATION, (err, decoded) => { 82 | if (err) { 83 | console.log('Activation error'); 84 | return res.status(401).json({ 85 | errors: 'Expired link. Signup again' 86 | }); 87 | } else { 88 | //If valid save to database 89 | //Get name, email, password from token 90 | const { 91 | name, 92 | email, 93 | password 94 | } = jwt.decode(token); 95 | 96 | console.log(email); 97 | const user = new User({ 98 | name, 99 | email, 100 | password 101 | }); 102 | 103 | user.save((err, user) => { 104 | if (err) { 105 | console.log('Save error', errorHandler(err)); 106 | return res.status(401).json({ 107 | errors: errorHandler(err) 108 | }); 109 | } else { 110 | return res.json({ 111 | success: true, 112 | message: 'Signup success', 113 | }); 114 | } 115 | }); 116 | } 117 | }); 118 | } else { 119 | return res.json({ 120 | message: 'Error Occured' 121 | }); 122 | } 123 | }; 124 | 125 | exports.loginController = (req, res) => { 126 | const { email, password } = req.body; 127 | const errors = validationResult(req); 128 | if (!errors.isEmpty()) { 129 | const firstError = errors.array().map(error => error.msg)[0]; 130 | return res.status(422).json({ 131 | errors: firstError 132 | }); 133 | } else { 134 | // check if user exist 135 | User.findOne({ 136 | email 137 | }).exec((err, user) => { 138 | if (err || !user) { 139 | return res.status(400).json({ 140 | errors: 'User does not exist. Please signup' 141 | }); 142 | } 143 | // authenticate 144 | if (!user.authenticate(password)) { 145 | return res.status(400).json({ 146 | errors: 'Email and password do not match' 147 | }); 148 | } 149 | // generate a token and send to client 150 | const token = jwt.sign( 151 | { 152 | _id: user._id 153 | }, 154 | process.env.JWT_SECRET, 155 | { 156 | expiresIn: '7d' 157 | } 158 | ); 159 | const { _id, name, email, role } = user; 160 | 161 | return res.json({ 162 | token, 163 | user: { 164 | _id, 165 | name, 166 | email, 167 | role 168 | } 169 | }); 170 | }); 171 | } 172 | }; 173 | 174 | exports.forgotPasswordController = (req, res) => { 175 | const { email } = req.body; 176 | const errors = validationResult(req); 177 | 178 | //Validation to req, body we will create custom validation in seconds 179 | if (!errors.isEmpty()) { 180 | const firstError = errors.array().map(error => error.msg)[0]; 181 | return res.status(422).json({ 182 | errors: firstError 183 | }); 184 | } else { 185 | //Find if user already exists 186 | User.findOne( 187 | { 188 | email 189 | }, 190 | (err, user) => { 191 | if (err || !user) { 192 | return res.status(400).json({ 193 | error: 'User does not exist' 194 | }); 195 | } 196 | 197 | const token = jwt.sign( 198 | { 199 | _id: user._id 200 | }, 201 | process.env.JWT_RESET_PASSWORD, 202 | { 203 | expiresIn: '10m' 204 | } 205 | ); 206 | 207 | const emailData = { 208 | from: process.env.EMAIL_FROM, 209 | to: email, 210 | subject: `Password Reset link`, 211 | html: ` 212 |

Please use the following link to reset your password

213 |

${process.env.CLIENT_URL}/users/password/reset/${token}

214 |
215 |

This email may contain sensetive information

216 |

${process.env.CLIENT_URL}

217 | ` 218 | }; 219 | 220 | return user.updateOne( 221 | { 222 | resetPasswordLink: token 223 | }, 224 | (err, success) => { 225 | if (err) { 226 | console.log('RESET PASSWORD LINK ERROR', err); 227 | return res.status(400).json({ 228 | error: 229 | 'Database connection error on user password forgot request' 230 | }); 231 | } else { 232 | sgMail 233 | .send(emailData) 234 | .then(sent => { 235 | // console.log('SIGNUP EMAIL SENT', sent) 236 | return res.json({ 237 | message: `Email has been sent to ${email}. Follow the instruction to activate your account` 238 | }); 239 | }) 240 | .catch(err => { 241 | // console.log('SIGNUP EMAIL SENT ERROR', err) 242 | return res.json({ 243 | message: err.message 244 | }); 245 | }); 246 | } 247 | } 248 | ); 249 | } 250 | ); 251 | } 252 | }; 253 | 254 | 255 | exports.resetPasswordController = (req, res) => { 256 | const { resetPasswordLink, newPassword } = req.body; 257 | 258 | const errors = validationResult(req); 259 | 260 | if (!errors.isEmpty()) { 261 | const firstError = errors.array().map(error => error.msg)[0]; 262 | return res.status(422).json({ 263 | errors: firstError 264 | }); 265 | } else { 266 | if (resetPasswordLink) { 267 | jwt.verify(resetPasswordLink, process.env.JWT_RESET_PASSWORD, function( 268 | err, 269 | decoded 270 | ) { 271 | if (err) { 272 | return res.status(400).json({ 273 | error: 'Expired link. Try again' 274 | }); 275 | } 276 | 277 | User.findOne( 278 | { 279 | resetPasswordLink 280 | }, 281 | (err, user) => { 282 | if (err || !user) { 283 | return res.status(400).json({ 284 | error: 'Something went wrong. Try later' 285 | }); 286 | } 287 | 288 | const updatedFields = { 289 | password: newPassword, 290 | resetPasswordLink: '' 291 | }; 292 | 293 | user = _.extend(user, updatedFields); 294 | 295 | user.save((err, result) => { 296 | if (err) { 297 | return res.status(400).json({ 298 | error: 'Error resetting user password' 299 | }); 300 | } 301 | res.json({ 302 | message: `Great! Now you can login with your new password` 303 | }); 304 | }); 305 | } 306 | ); 307 | }); 308 | } 309 | } 310 | }; 311 | 312 | exports.adminMiddleware = (req, res, next) => { 313 | User.findById({ 314 | _id: req.user._id 315 | }).exec((err, user) => { 316 | if (err || !user) { 317 | return res.status(400).json({ 318 | error: 'User not found' 319 | }); 320 | } 321 | 322 | if (user.role !== 'admin') { 323 | return res.status(400).json({ 324 | error: 'Admin resource. Access denied.' 325 | }); 326 | } 327 | 328 | req.profile = user; 329 | next(); 330 | }); 331 | }; 332 | 333 | 334 | 335 | exports.requireSignin = expressJwt({ 336 | secret: process.env.JWT_SECRET // req.user._id 337 | }); 338 | -------------------------------------------------------------------------------- /controllers/user.controller.js: -------------------------------------------------------------------------------- 1 | const User = require('../models/auth.model'); 2 | const expressJwt = require('express-jwt'); 3 | 4 | exports.readController = (req, res) => { 5 | const userId = req.params.id; 6 | User.findById(userId).exec((err, user) => { 7 | if (err || !user) { 8 | return res.status(400).json({ 9 | error: 'User not found' 10 | }); 11 | } 12 | user.hashed_password = undefined; 13 | user.salt = undefined; 14 | res.json(user); 15 | }); 16 | }; 17 | 18 | exports.updateController = (req, res) => { 19 | 20 | // console.log('UPDATE USER - req.user', req.user, 'UPDATE DATA', req.body); 21 | const { name, password } = req.body; 22 | 23 | User.findOne({ _id: req.user._id }, (err, user) => { 24 | if (err || !user) { 25 | return res.status(400).json({ 26 | error: 'User not found' 27 | }); 28 | } 29 | if (!name) { 30 | return res.status(400).json({ 31 | error: 'Name is required' 32 | }); 33 | } else { 34 | user.name = name; 35 | } 36 | 37 | if (password) { 38 | if (password.length < 6) { 39 | return res.status(400).json({ 40 | error: 'Password should be min 6 characters long' 41 | }); 42 | } else { 43 | user.password = password; 44 | } 45 | } 46 | 47 | user.save((err, updatedUser) => { 48 | if (err) { 49 | console.log('USER UPDATE ERROR', err); 50 | return res.status(400).json({ 51 | error: 'User update failed' 52 | }); 53 | } 54 | updatedUser.hashed_password = undefined; 55 | updatedUser.salt = undefined; 56 | res.json(updatedUser); 57 | }); 58 | }); 59 | }; -------------------------------------------------------------------------------- /helpers/dbErrorHandling.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | /* 4 | Get Unique error field name 5 | */ 6 | 7 | const uniqueMessage = error => { 8 | let output; 9 | try { 10 | let fieldName = error.message.split(".$")[1] 11 | field = field.split("dub key")[0] 12 | req.flash("errors",[{ 13 | message: "An account with this"+ field +"already exists" 14 | }]) 15 | 16 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + "already exists" 17 | } catch(err) { 18 | output = "already exists" 19 | } 20 | 21 | return output 22 | } 23 | 24 | /* 25 | Get the error message from error object 26 | */ 27 | 28 | exports.errorHandler = error => { 29 | let message = '' 30 | if(error.code) { 31 | switch(error.code) { 32 | case 11000: 33 | case 11001: 34 | message = uniqueMessage(error); 35 | break; 36 | default: 37 | message = "Something went wrong"; 38 | } 39 | } else { 40 | for (let errorName in error.errors) { 41 | if(error.errors[errorName].message) 42 | message = error.errors[errorName].message; 43 | } 44 | 45 | return message; 46 | }; 47 | } -------------------------------------------------------------------------------- /helpers/valid.js: -------------------------------------------------------------------------------- 1 | //Validation Helpers 2 | const { 3 | check 4 | } = require('express-validator'); 5 | 6 | //Register 7 | exports.validSign = [ 8 | check('name', 'Name is required').notEmpty() 9 | .isLength({ 10 | min: 4, 11 | max: 32 12 | }).withMessage('name must be between 3 to 32 characters'), 13 | check('email') 14 | .isEmail() 15 | .withMessage('Must be a valid email address'), 16 | check('password', 'password is required').notEmpty(), 17 | check('password').isLength({ 18 | min: 6 19 | }).withMessage('Password must contain at least 6 characters').matches(/\d/).withMessage('password must contain a number') 20 | ] 21 | 22 | //Login 23 | exports.validLogin = [ 24 | check('email') 25 | .isEmail() 26 | .withMessage('Must be a valid email address'), 27 | check('password', 'password is required').notEmpty(), 28 | check('password').isLength({ 29 | min: 6 30 | }).withMessage('Password must contain at least 6 characters').matches(/\d/).withMessage('password must contain a number') 31 | ] 32 | 33 | //ForgetPassword 34 | exports.forgotPasswordValidator = [ 35 | check('email') 36 | .not() 37 | .isEmpty() 38 | .isEmail() 39 | .withMessage('Must be a valid email address') 40 | ]; 41 | 42 | //ResetPassword 43 | exports.resetPasswordValidator = [ 44 | check('newPassword') 45 | .not() 46 | .isEmpty() 47 | .isLength({ min: 6 }) 48 | .withMessage('Password must be at least 6 characters long') 49 | ]; -------------------------------------------------------------------------------- /models/auth.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const crypto = require('crypto'); 3 | // user schema 4 | const userSchema = new mongoose.Schema( 5 | { 6 | email: { 7 | type: String, 8 | trim: true, 9 | required: true, 10 | unique: true, 11 | lowercase: true 12 | }, 13 | name: { 14 | type: String, 15 | trim: true, 16 | required: true 17 | }, 18 | hashed_password: { 19 | type: String, 20 | required: true 21 | }, 22 | salt: String, 23 | role: { 24 | type: String, 25 | default: 'Employer' 26 | }, 27 | resetPasswordLink: { 28 | data: String, 29 | default: '' 30 | } 31 | }, 32 | { 33 | timestamps: true 34 | } 35 | ); 36 | 37 | // virtual 38 | userSchema 39 | .virtual('password') 40 | .set(function(password) { 41 | this._password = password; 42 | this.salt = this.makeSalt(); 43 | this.hashed_password = this.encryptPassword(password); 44 | }) 45 | .get(function() { 46 | return this._password; 47 | }); 48 | 49 | // methods 50 | userSchema.methods = { 51 | authenticate: function(plainText) { 52 | return this.encryptPassword(plainText) === this.hashed_password; 53 | }, 54 | 55 | encryptPassword: function(password) { 56 | if (!password) return ''; 57 | try { 58 | return crypto 59 | .createHmac('sha1', this.salt) 60 | .update(password) 61 | .digest('hex'); 62 | } catch (err) { 63 | return ''; 64 | } 65 | }, 66 | 67 | makeSalt: function() { 68 | return Math.round(new Date().valueOf() * Math.random()) + ''; 69 | } 70 | }; 71 | 72 | module.exports = mongoose.model('User', userSchema); 73 | -------------------------------------------------------------------------------- /models/certauth.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require ('mongoose') 2 | 3 | const TransactionSchema = new mongoose.Schema( 4 | { 5 | usn: { 6 | type: String, 7 | trim: true 8 | }, 9 | tid: { 10 | type: String, 11 | trim: true 12 | }, 13 | email: { 14 | type: String, 15 | trim: true 16 | }, 17 | cert_id: { 18 | type: String, 19 | trim: true 20 | } 21 | } 22 | ); 23 | 24 | module.exports = mongoose.model('Certificate', TransactionSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "NODE_ENV=production node server", 8 | "dev": "nodemon server" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@sendgrid/mail": "^7.0.1", 15 | "body-parser": "^1.19.0", 16 | "cors": "^2.8.5", 17 | "dotenv": "^8.2.0", 18 | "express": "^4.17.1", 19 | "express-jwt": "^5.3.3", 20 | "express-validator": "^6.4.0", 21 | "google-auth-library": "^6.0.0", 22 | "jsonwebtoken": "^8.5.1", 23 | "loadash": "^1.0.0", 24 | "lodash": "^4.17.15", 25 | "mongoose": "^5.9.9", 26 | "morgan": "^1.10.0", 27 | "node-fetch": "^2.6.0", 28 | "nodemon": "^2.0.3", 29 | "object-hash": "^2.0.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /routes/auth.route.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | 4 | //Validation 5 | const { 6 | validSign, 7 | validLogin, 8 | forgotPasswordValidator, 9 | resetPasswordValidator 10 | } = require('../helpers/valid') 11 | 12 | //Load controllers 13 | const { 14 | registerController, 15 | loginController, 16 | activationController, 17 | forgotPasswordController, 18 | resetPasswordController 19 | } = require('../controllers/auth.controller.js') 20 | 21 | router.post('/register', validSign, registerController) 22 | router.post('/login', validLogin, loginController) 23 | router.post('/activation', activationController) 24 | router.put('/password/forget', forgotPasswordValidator, forgotPasswordController) 25 | router.put('/password/reset', resetPasswordValidator, resetPasswordController) 26 | module.exports = router -------------------------------------------------------------------------------- /routes/user.route.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | // import controller 5 | const { requireSignin, adminMiddleware } = require('../controllers/auth.controller'); 6 | const { readController, updateController } = require('../controllers/user.controller'); 7 | 8 | router.get('/user/:id', requireSignin, readController); 9 | router.put('/user/update', requireSignin, updateController); 10 | router.put('/admin/update', requireSignin, adminMiddleware, updateController); 11 | 12 | module.exports = router; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const morgan = require('morgan') 3 | const bodyparser = require('body-parser') 4 | const cors = require('cors') 5 | const app = express() 6 | const mongoose = require ('mongoose') 7 | 8 | const sgMail = require('@sendgrid/mail'); 9 | 10 | const transaction = require('./models/certauth.model'); 11 | const { errorHandler } = require('./helpers/dbErrorHandling') 12 | 13 | //Config .env to ./config/config.env 14 | require('dotenv').config({ 15 | path:'./config/config.env' 16 | }) 17 | 18 | //Connect to Database 19 | const uri = process.env.MONGO_URI 20 | mongoose.connect(uri, { 21 | useNewUrlParser: true, 22 | useCreateIndex: true, 23 | useUnifiedTopology: true 24 | } 25 | ); 26 | 27 | const connection = mongoose.connection; 28 | connection.once('open', () => { 29 | console.log ("MongoDB database connection established successfully"); 30 | }) 31 | 32 | //Use bodyParser 33 | app.use(bodyparser.json()) 34 | 35 | //Load all routes 36 | const authRouter = require('./routes/auth.route') 37 | const userRouter = require('./routes/user.route') 38 | 39 | //config for only development 40 | if(process.env.NODE_ENV === 'development') { 41 | app.use(cors({ 42 | origin: process.env.CLIENT_URL 43 | })) 44 | 45 | app.use(morgan('dev')) 46 | //Morgan give information about each request 47 | //Cors it's allow to deal with react for localhost at port 3000 without any problem 48 | } 49 | //Use Routes 50 | app.use('/api/',authRouter); 51 | app.use('/api', userRouter); 52 | 53 | 54 | //client-mail 55 | app.post('/api/smail', (req, res, next) => { 56 | sgMail.setApiKey(process.env.MAIL_KEY); 57 | //console.log(req.body) 58 | //console.log(req.body.z) 59 | const email = req.body.z 60 | const emailData = { 61 | to: email, 62 | from: process.env.EMAIL_FROM, 63 | fromname: 'SADG University', 64 | subject: 'Graduation Certificate', 65 | html: ` 66 |

Greetings from SADG University

67 |
68 |

Hello ${req.body.y}

69 |
70 | This is your certificate id : ${req.body.x} 71 |
72 | Click here to view Certificate 73 |
74 |

Management, Principal, Faculty Congratulates you

75 |
76 |

We wish you All the Best for your Future Endeavours

77 | < 78 | `, 79 | }; 80 | 81 | sgMail.send(emailData).then(sent => { 82 | return res.json({ 83 | message: `Email has been sent to ${email}` 84 | }); 85 | }).catch(err => { 86 | return res.status(400).json({ 87 | success: false 88 | }); 89 | }); 90 | 91 | const tid = req.body.t 92 | const usn = req.body.u 93 | const cert_id = req.body.x 94 | 95 | console.log(tid) 96 | 97 | const certx = new transaction({ 98 | usn, 99 | tid, 100 | email, 101 | cert_id 102 | }); 103 | 104 | certx.save((err, certx) => { 105 | if (err) { 106 | console.log('Save error', errorHandler(err)); 107 | return res.status(401).json({ 108 | errors: errorHandler(err) 109 | }); 110 | } else { 111 | return res.json({ 112 | success: true, 113 | message: 'Transaction details sent!!', 114 | }); 115 | } 116 | }); 117 | }) 118 | 119 | app.post('/api/idfetch', (req, res, next) => { 120 | const usn = req.body.id 121 | //console.log(usn) 122 | transaction.findOne({ 123 | usn 124 | }).exec((err, tid) => { 125 | return res.send(tid) 126 | }) 127 | }); 128 | 129 | app.get('/api/getrapi', (req, res, next) => { 130 | return res.send(process.env.INFURA_API_KEY) 131 | }); 132 | 133 | app.get('/api/rdefault', (req, res, next) => { 134 | return res.send(process.env.ROPSTEN_DEFAULT_ACCOUNT) 135 | }); 136 | 137 | 138 | 139 | app.use((req, res, next) => { 140 | res.status(404).json({ 141 | success: false, 142 | message: "Page not found" 143 | }) 144 | }); 145 | const PORT = process.env.PORT 146 | 147 | var listener = app.listen(PORT, function() { 148 | console.log(`App listening on port ${PORT}`); //Listening on port 8888 149 | }); --------------------------------------------------------------------------------