├── README.md ├── config └── dev.env ├── package-lock.json ├── package.json ├── public ├── css │ ├── admin.css │ └── index.css ├── index.html └── js │ ├── admin.js │ └── index.js ├── src ├── db │ └── mongoose.js ├── frontend │ ├── controllers │ │ ├── admin.js │ │ ├── bet.js │ │ ├── chat.js │ │ ├── index.js │ │ ├── message.js │ │ ├── user.js │ │ └── values.js │ └── views │ │ ├── admin.js │ │ ├── base.js │ │ ├── bet.js │ │ └── user.js ├── index.js ├── middleware │ ├── adminAuth.js │ ├── auth.js │ └── chatAuth.js ├── models │ ├── bet.js │ ├── chat.js │ └── user.js ├── private │ └── admin.html ├── routers │ ├── admin.js │ ├── bet.js │ ├── chat.js │ └── user.js └── utils │ ├── chat.js │ ├── clientSeed.js │ ├── pf.js │ └── serverSeed.js └── webpack.config.js /README.md: -------------------------------------------------------------------------------- 1 | # dice 2 | Provably Fair Dice Script with chat box written in Node.JS, MongoDB and Socket.IO 3 | 4 | Written as a single page application providing complete crypto gambling experience like professional crypto casinos. 5 | 6 | Minimal frontend is written in pure Javascript which is not optimized for mobile view. 7 | 8 | Check Implementation Here: [casino.webtricks.website](https://casino.webtricks.website) 9 | 10 | Test Admin Panel: [dummy.webtricks.website](http://dummy.webtricks.website). Username: 'admin' Password: 'admin123' 11 | 12 | Created By: [webtricks](https://bitcointalk.org/index.php?action=profile;u=921974) 13 | 14 | **Note: I have written the whole script in just over 4 hours and haven't tested any code so make sure to test each and every line of code if you plan to use any part of this script for PRODUCTION PURPOSE.** 15 | 16 | --- 17 | 18 | ## How to run script locally 19 | ``` 20 | $ git clone https://github.com/web-tricks/dice 21 | $ cd dice 22 | $ npm install 23 | # (make sure to install MongoDB before moving to next step. If you don't know how to install, please check YouTube guide, there are many) 24 | # (Once MongoDB is installed and running locally, edit database name in `config/dev.env` file. 25 | # Example: MONGOOSE=mongodb://127.0.0.1:27017/casino-database) 26 | $ npm run dev 27 | ``` 28 | --- 29 | 30 | ## How to run script on web 31 | You any cloud MongoDB service provider like MongoDB Atlas. Set-up production database. Retrieve database string and set up MONGOOSE environment variable equals to that string. 32 | 33 | --- 34 | ## Other Informations: 35 | 1. To edit front-end of the script. Use `src/frontend` folder. To save changes, run `npm run build` command. 36 | 2. I have uploaded `config` folder which includes dev.env file which is intended to use only for development purposes. Make sure to delete that file and use your own environment variables if you plan to use script in any way. 37 | 3. Anyone is free to use this script for whatever purpose he/she likes. There is no obligation. 38 | 39 | 40 | __BTC Address: 3G8uPe3XKGhyQ2HGZfrmT7yJSxeQ6kJHK4__ 41 | -------------------------------------------------------------------------------- /config/dev.env: -------------------------------------------------------------------------------- 1 | MONGOOSE=mongodb://127.0.0.1:27017/webtricks 2 | PORT=3000 3 | JWT_TOKEN=bellaciaobellaciao 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "caseeno", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "env-cmd -f ./config/dev.env nodemon src/index.js", 8 | "start": "node src/index.js", 9 | "build": "webpack --mode production" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "axios": "^0.19.2", 16 | "bad-words": "^3.0.3", 17 | "bcryptjs": "^2.4.3", 18 | "cookie-parser": "^1.4.5", 19 | "env-cmd": "^10.1.0", 20 | "express": "^4.17.1", 21 | "jsonwebtoken": "^8.5.1", 22 | "moment": "^2.25.3", 23 | "mongoose": "^5.9.13", 24 | "number-precision": "^1.3.2", 25 | "socket.io": "^2.3.0", 26 | "validator": "^13.7.0", 27 | "webpack": "^4.43.0" 28 | }, 29 | "devDependencies": { 30 | "webpack-cli": "^3.3.11" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /public/css/admin.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | text-align: center; 3 | font-family: Arial, Helvetica, sans-serif; 4 | text-transform: uppercase; 5 | } 6 | 7 | .big__div { 8 | width: 30%; 9 | margin-left: 1%; 10 | float: left; 11 | padding: 8px; 12 | text-align: center; 13 | height: 200px; 14 | margin-top: auto; 15 | border-radius: 2%; 16 | } 17 | 18 | .big__div h2 { 19 | margin-top: 50px; 20 | text-transform: uppercase; 21 | color: #ffffff; 22 | font-family: Arial, Helvetica, sans-serif; 23 | } 24 | 25 | .big__div button, .big__div input[type="submit"] { 26 | padding: 5px 15px; 27 | background-color: rgba(255, 255, 255, 0.2); 28 | border: 1px solid #ffffff; 29 | color: #ffffff; 30 | border-radius: 5px; 31 | font-size: 16px; 32 | margin-top: 16px; 33 | cursor: pointer; 34 | } 35 | 36 | .big__div input[type="text"] { 37 | padding: 5px 15px; 38 | background-color: rgba(255, 255, 255); 39 | border: 1px solid #ffffff; 40 | color: #000000; 41 | font-size: 16px; 42 | } 43 | 44 | .first { 45 | background-color: #4169E2; 46 | } 47 | 48 | .second { 49 | background-color: #FE003E; 50 | } 51 | 52 | .third { 53 | background-color: #008001; 54 | } 55 | 56 | .hold__button { 57 | float: right; 58 | margin-right: 4%; 59 | } 60 | 61 | .output__display { 62 | clear: both; 63 | text-align: center; 64 | } 65 | 66 | .hold__button button { 67 | padding: 5px 15px; 68 | background-color: rgba(0, 0, 0, 0.1); 69 | border: 1px solid #000000; 70 | color: #000000; 71 | border-radius: 5px; 72 | font-size: 16px; 73 | margin-top: 16px; 74 | cursor: pointer; 75 | } 76 | 77 | .output__display span { 78 | display: inline-block; 79 | margin-top: 4px; 80 | } 81 | 82 | .submit-style, .output__display button { 83 | margin-left: 8px; 84 | background-color: transparent; 85 | border: 1px solid #000000; 86 | color: #000000; 87 | margin-top: 16px; 88 | cursor: pointer; 89 | } 90 | 91 | .output__display form { 92 | position: relative; 93 | top: 48px; 94 | } 95 | 96 | .output__display input { 97 | margin-top: 16px; 98 | } 99 | 100 | footer { 101 | height: 40px; 102 | position: fixed; 103 | bottom:0%; 104 | width:100%; 105 | } -------------------------------------------------------------------------------- /public/css/index.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 90%; 3 | } 4 | 5 | .tools { 6 | float: right; 7 | } 8 | 9 | .game__body, .chat__body { 10 | float: left; 11 | margin-right: 2%; 12 | } 13 | 14 | header, .game__body { 15 | width: 70%; 16 | } 17 | 18 | .chat__body { 19 | width: 25%; 20 | height: 100%; 21 | border: 1px solid saddlebrown; 22 | } 23 | 24 | .register__form, .login__form, .verify__data { 25 | width: 30%; 26 | height: 200px; 27 | position: absolute; 28 | margin: auto; 29 | top:0; 30 | bottom: 0; 31 | left: 0; 32 | right: 0; 33 | z-index: 50; 34 | text-align: center; 35 | padding: 20px; 36 | background-color: wheat; 37 | } 38 | 39 | .black__back { 40 | position: absolute; 41 | width: 100%; 42 | height: 100%; 43 | top:0; 44 | bottom: 0; 45 | left: 0; 46 | right: 0; 47 | opacity: 0.6; 48 | background-color: #000000; 49 | z-index: 30; 50 | 51 | } 52 | 53 | .register__form input, .login__form input { 54 | width: 95%; 55 | height: 24px; 56 | border-radius: 5%; 57 | border: 1px solid brown; 58 | } 59 | 60 | .title { 61 | font-weight: bold; 62 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 63 | font-size: 28px; 64 | color: rgb(63, 27, 11); 65 | } 66 | 67 | .btn__style { 68 | background-color: transparent; 69 | border-radius: 5px; 70 | padding: 5px 10px; 71 | border: 1px solid sienna; 72 | cursor: pointer; 73 | font-size: medium; 74 | } 75 | 76 | .game__body { 77 | margin-top: 48px; 78 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; 79 | font-size: medium; 80 | text-align: center; 81 | margin-left: 8px; 82 | background-color: #FFF9F7; 83 | } 84 | 85 | .client__span { 86 | float: right; 87 | margin-right: 16px; 88 | } 89 | 90 | .server__span { 91 | float: left; 92 | margin-left: 16px; 93 | } 94 | 95 | .login__button, .bet__verify { 96 | color: sienna; 97 | font-size: medium; 98 | font-weight: bold; 99 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 100 | cursor: pointer; 101 | margin-left: 12px; 102 | } 103 | 104 | .inp__style, .message__input { 105 | background-color: rgb(250, 245, 245); 106 | border-radius: 5px; 107 | padding: 5px 10px; 108 | border: 1px solid sienna; 109 | font-size: medium; 110 | margin-top: 16px; 111 | } 112 | 113 | .gaming__tools { 114 | margin-top: 48px; 115 | } 116 | 117 | .chat__messages { 118 | height: 90%; 119 | overflow: auto; 120 | } 121 | 122 | 123 | .slider { 124 | -webkit-appearance: none; 125 | width: 75%; 126 | height: 20px; 127 | margin-top: 48px; 128 | background-color: #FFD5BA; 129 | outline: none; 130 | opacity: 0.7; 131 | -webkit-transition: .2s; 132 | transition: opacity .2s; 133 | } 134 | 135 | .slider:hover { 136 | opacity: 1; 137 | } 138 | 139 | .slider::-webkit-slider-thumb { 140 | -webkit-appearance: none; 141 | appearance: none; 142 | width: 25px; 143 | height: 35px; 144 | background: sienna; 145 | cursor: pointer; 146 | } 147 | 148 | .slider::-moz-range-thumb { 149 | width: 25px; 150 | height: 25px; 151 | background: sienna; 152 | cursor: pointer; 153 | } 154 | 155 | .slider:hover { 156 | opacity: 1; 157 | } 158 | 159 | .shift { 160 | position: relative; 161 | font-size: medium; 162 | border: none; 163 | padding: 5px 10px; 164 | color: #FFFFFF; 165 | background-color: sienna; 166 | cursor: pointer; 167 | left: -8px; 168 | } 169 | 170 | .roll__button { 171 | margin-top: 16px; 172 | font-size: larger; 173 | width: 100px; 174 | } 175 | 176 | .roll__button:hover { 177 | background-color: sienna; 178 | color: #FFFFFF; 179 | } 180 | 181 | .credits { 182 | font-size: larger; 183 | } 184 | 185 | .message__input { 186 | margin-left: 4%; 187 | width: 70%; 188 | } 189 | 190 | .send__button { 191 | position: relative; 192 | left: -2%; 193 | width: 18%; 194 | background-color: sienna; 195 | padding: 5px 10px; 196 | border: 1px solid sienna; 197 | font-size: medium; 198 | margin-top: 16px; 199 | color: #FFF9F7; 200 | border-top-right-radius: 5px; 201 | border-bottom-right-radius: 5px; 202 | } 203 | 204 | .server__box { 205 | width: 450px; 206 | font-size: small; 207 | } 208 | 209 | .bet { 210 | display: block; 211 | margin-top: 8px; 212 | } 213 | 214 | .bets { 215 | height: 250px; 216 | overflow: auto; 217 | } 218 | 219 | .verify__data { 220 | width: 80%; 221 | text-align: left; 222 | } 223 | 224 | .close__verify { 225 | cursor: pointer; 226 | } 227 | 228 | .message { 229 | border-bottom: 1px solid sienna; 230 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; 231 | font-size: small; 232 | } 233 | 234 | .message__meta { 235 | float: right; 236 | margin-right: 4%; 237 | } 238 | 239 | .message__name { 240 | margin-left: 4%; 241 | font-style: italic; 242 | color: sienna; 243 | } 244 | 245 | .message__main { 246 | font-size: medium; 247 | margin-left: 4%; 248 | margin-right: 4%; 249 | } 250 | 251 | .register__instead { 252 | cursor: pointer; 253 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Webtricks Casino 7 | 8 | 9 | 10 |
11 | WEBTRICKS CASINO 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 | 20 | Server Seed Hash: 21 | 22 | 23 | 24 | Client Seed: 25 |
26 | (Edit above to set your client seed) 27 |
28 |
29 |
30 | Bet Amount: 31 | 32 | Profit: 33 |
34 | Roll 35 | under: 36 | 37 | 38 | Payout: 39 | 40 | Win Chance: 41 |
42 | 43 |
44 |
45 |
46 |

My Recent Bets

47 |
48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 |
58 |
59 |
60 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /public/js/admin.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=380)}({16:function(e,t){e.exports={slider:document.querySelector(".slider"),rollBox:document.querySelector(".roll__box"),payoutBox:document.querySelector(".payout__box"),chanceBox:document.querySelector(".chance__box"),shift:document.querySelector(".shift"),rollText:document.querySelector(".roll__text"),profitBox:document.querySelector(".profit__box"),betBox:document.querySelector(".bet__box"),registerButton:document.querySelector(".register__button"),loginButton:document.querySelector(".login__button"),overlayBox:document.querySelector(".overlay__box"),tools:document.querySelector(".tools"),serverBox:document.querySelector(".server__box"),clientBox:document.querySelector(".client__box"),gameBody:document.querySelector(".game__body"),rollButton:document.querySelector(".roll__button"),resultBox:document.querySelector(".result__box"),myBetBox:document.querySelector(".my__bets"),loadBets:document.querySelector(".load__bets"),sendMessage:document.querySelector(".send__message"),messageInput:document.querySelector(".message__input"),chatButton:document.querySelector(".send__button"),chatArea:document.querySelector(".chat__messages"),membersCount:document.querySelector(".members__count"),adminTopBody:document.querySelector(".admin__body-top"),showMembersButton:document.querySelector(".show__members-button"),adminDisplayResult:document.querySelector(".display__result"),adminUserForm:document.querySelector(".member__search-form"),adminBetsForm:document.querySelector(".bet__search-form"),adminUserInput:document.querySelector(".member__search-input"),adminBetsInput:document.querySelector(".bet__search-input"),holdButtonDiv:document.querySelector(".hold__button"),adminOutputDisplay:document.querySelector(".output__display")}},17:function(e,t,n){e.exports=n(45)},26:function(e,t,n){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},r.forEach(["delete","get","head"],(function(e){u.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){u.headers[e]=r.merge(s)})),e.exports=u}).call(this,n(5))},3:function(e,t,n){"use strict";var r=n(26),o=Object.prototype.toString;function s(e){return"[object Array]"===o.call(e)}function a(e){return void 0===e}function i(e){return null!==e&&"object"==typeof e}function u(e){return"[object Function]"===o.call(e)}function c(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),s(e))for(var n=0,r=e.length;n{try{return(await r.get("/users/details?skip="+e)).data}catch(e){alert(e.response.data)}},c=async(e,t)=>{try{return(await r.get(`/user/getinfo/${t}/${e}`)).data}catch(e){alert(e.response.data)}},l=async(e,t)=>{try{return(await r.get(`/user/allbets/${t}?skip=${e}`)).data}catch(e){alert(e.response.data)}};window.addEventListener("DOMContentLoaded",()=>{(async e=>{try{const t=await r.get("/users/all");e.insertAdjacentHTML("beforeend",t.data)}catch(e){alert(e.response.data)}})(o.membersCount)}),o.adminTopBody.addEventListener("click",async e=>{if(e.target===o.showMembersButton){a.splice(0,a.length),o.adminOutputDisplay.innerHTML="",o.holdButtonDiv.innerHTML="";const e=await u(a.length);e&&(s.loadMoreButton(o.holdButtonDiv,"Users"),e.forEach((e,t)=>{a.push(e),s.displayUser(o.adminOutputDisplay,e,t+1)}))}}),o.adminTopBody.addEventListener("submit",async e=>{if(e.target===o.adminUserForm){e.preventDefault(),o.adminOutputDisplay.innerHTML="",o.holdButtonDiv.innerHTML="";const t=await c(o.adminUserInput.value,"username");t&&s.displayUserForm(o.adminOutputDisplay,t)}else if(e.target===o.adminBetsForm){e.preventDefault(),i.splice(0,i.length),o.adminOutputDisplay.innerHTML="",o.holdButtonDiv.innerHTML="";const t=await l(i.length,o.adminBetsInput.value);t&&(s.loadMoreButton(o.holdButtonDiv,"Bets"),t.forEach(e=>{i.push(e),s.displayBet(o.adminOutputDisplay,e)}))}}),o.adminDisplayResult.addEventListener("click",async e=>{if(e.target.matches(".edit__member")){const t=e.target.closest(".user__span").dataset.id,n=await c(t,"id");n&&(o.adminOutputDisplay.innerHTML="",o.holdButtonDiv.innerHTML="",s.displayUserForm(o.adminOutputDisplay,n))}else if(e.target.matches(".load__more-Bets")){const e=await l(i.length,o.adminBetsInput.value);e&&e.forEach(e=>{i.push(e),s.displayBet(o.adminOutputDisplay,e)})}else if(e.target.matches(".load__more-Users")){const e=await u(a.length);e&&e.forEach((e,t)=>{a.push(e),s.displayUser(o.adminOutputDisplay,e,t+1)})}}),o.adminDisplayResult.addEventListener("submit",async e=>{if(e.target.matches(".user__edit-form")){e.preventDefault();const t=e.target.dataset.id,n=await(async e=>{const t=document.querySelector(".edit__password").value;if(t.length>0&&t.length<8)return alert("Password Length must be greater than 7 characters.");const n=0!==t.length?t:void 0,o=document.querySelector(".edit__balance").value;if(isNaN(o))return alert("Wrong Value for Balance");const s={email:document.querySelector(".edit__email").value,role:document.querySelector(".edit__role").value,balance:parseFloat(o)},a=n?Object.assign(s,{password:n}):s;try{return(await r.post("/user/edit/"+e,a)).data}catch(e){alert(e.response.data)}})(t);n&&(o.adminOutputDisplay.innerHTML="",o.holdButtonDiv.innerHTML="",n.forEach(e=>o.adminOutputDisplay.insertAdjacentHTML("beforeend",e+"
")))}})},381:function(e,t){e.exports={displayUser:(e,t,n)=>{const r=`\n \n ${n}. ${t.username} \n \n
\n `;e.insertAdjacentHTML("beforeend",r)},displayUserForm:(e,t)=>{const n=`\n
\n Username:
\n Email: \n Password:
\n Role: \n Balance:
\n \n
\n `;e.innerHTML=n},displayBet:(e,t)=>{const n=`\n \n ${t.choice} | \n ${t.result} |\n ${t.won} |\n ${t.payout.toFixed(8)}\n
\n `;e.insertAdjacentHTML("beforeend",n)},loadMoreButton:(e,t)=>{const n=`\n \n `;e.insertAdjacentHTML("beforeend",n)}}},45:function(e,t,n){"use strict";var r=n(3),o=n(26),s=n(46),a=n(32);function i(e){var t=new s(e),n=o(s.prototype.request,t);return r.extend(n,s.prototype,t),r.extend(n,t),n}var u=i(n(29));u.Axios=s,u.create=function(e){return i(a(u.defaults,e))},u.Cancel=n(33),u.CancelToken=n(59),u.isCancel=n(28),u.all=function(e){return Promise.all(e)},u.spread=n(60),e.exports=u,e.exports.default=u},46:function(e,t,n){"use strict";var r=n(3),o=n(27),s=n(47),a=n(48),i=n(32);function u(e){this.defaults=e,this.interceptors={request:new s,response:new s}}u.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{}).url=arguments[0]:e=e||{},(e=i(this.defaults,e)).method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach((function(e){t.unshift(e.fulfilled,e.rejected)})),this.interceptors.response.forEach((function(e){t.push(e.fulfilled,e.rejected)}));t.length;)n=n.then(t.shift(),t.shift());return n},u.prototype.getUri=function(e){return e=i(this.defaults,e),o(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},r.forEach(["delete","get","head","options"],(function(e){u.prototype[e]=function(t,n){return this.request(r.merge(n||{},{method:e,url:t}))}})),r.forEach(["post","put","patch"],(function(e){u.prototype[e]=function(t,n,o){return this.request(r.merge(o||{},{method:e,url:t,data:n}))}})),e.exports=u},47:function(e,t,n){"use strict";var r=n(3);function o(){this.handlers=[]}o.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){r.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o},48:function(e,t,n){"use strict";var r=n(3),o=n(49),s=n(28),a=n(29);function i(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){return i(e),e.headers=e.headers||{},e.data=o(e.data,e.headers,e.transformRequest),e.headers=r.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||a.adapter)(e).then((function(t){return i(e),t.data=o(t.data,t.headers,e.transformResponse),t}),(function(t){return s(t)||(i(e),t&&t.response&&(t.response.data=o(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},49:function(e,t,n){"use strict";var r=n(3);e.exports=function(e,t,n){return r.forEach(n,(function(n){e=n(e,t)})),e}},5:function(e,t){var n,r,o=e.exports={};function s(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function i(e){if(n===setTimeout)return setTimeout(e,0);if((n===s||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:s}catch(e){n=s}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var u,c=[],l=!1,f=-1;function d(){l&&u&&(l=!1,u.length?c=u.concat(c):f=-1,c.length&&p())}function p(){if(!l){var e=i(d);l=!0;for(var t=c.length;t;){for(u=c,c=[];++f1)for(var n=1;n=0)return;a[t]="set-cookie"===t?(a[t]?a[t]:[]).concat([n]):a[t]?a[t]+", "+n:n}})),a):a}},57:function(e,t,n){"use strict";var r=n(3);e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function o(e){var r=e;return t&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return e=o(window.location.href),function(t){var n=r.isString(t)?o(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return!0}},58:function(e,t,n){"use strict";var r=n(3);e.exports=r.isStandardBrowserEnv()?{write:function(e,t,n,o,s,a){var i=[];i.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&i.push("expires="+new Date(n).toGMTString()),r.isString(o)&&i.push("path="+o),r.isString(s)&&i.push("domain="+s),!0===a&&i.push("secure"),document.cookie=i.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},59:function(e,t,n){"use strict";var r=n(33);function o(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var n=this;e((function(e){n.reason||(n.reason=new r(e),t(n.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var e;return{token:new o((function(t){e=t})),cancel:e}},e.exports=o},60:function(e,t,n){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}}); -------------------------------------------------------------------------------- /src/db/mongoose.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | mongoose.connect(process.env.MONGOOSE, { 4 | useNewUrlParser: true, 5 | useCreateIndex: true, 6 | useUnifiedTopology: true 7 | }); -------------------------------------------------------------------------------- /src/frontend/controllers/admin.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const elements = require('../views/base'); 3 | const adminView = require('../views/admin'); 4 | 5 | /*----------------------- Variables (Holder) -----------------------*/ 6 | const allUsernames = []; 7 | const allBets = []; 8 | 9 | /*-------------------------- Functions -----------------------------*/ 10 | 11 | //Function to retrieve number of users 12 | const membersCount = async(element) => { 13 | try { 14 | const response = await axios.get('/users/all'); 15 | element.insertAdjacentHTML('beforeend', response.data); 16 | } catch(e) { 17 | alert(e.response.data); 18 | } 19 | } 20 | 21 | //Function to retrieve usernames of all members 22 | const membersUsername = async(length) => { 23 | try { 24 | const response = await axios.get(`/users/details?skip=${length}`); 25 | return response.data; 26 | } catch(e) { 27 | alert(e.response.data); 28 | } 29 | } 30 | 31 | //Function to retrieve details of single user through id or username 32 | const getMemberDetails = async(id,type) => { 33 | try { 34 | const response = await axios.get(`/user/getinfo/${type}/${id}`); 35 | return response.data; 36 | } catch(e) { 37 | alert(e.response.data); 38 | } 39 | } 40 | 41 | //Function to retrieve bets of single user 42 | const getMemberBets = async(length,username) => { 43 | try { 44 | const response = await axios.get(`/user/allbets/${username}?skip=${length}`); 45 | return response.data; 46 | } catch(e) { 47 | alert(e.response.data); 48 | } 49 | } 50 | 51 | //Function to update user 52 | const updateUser = async(id) => { 53 | const passwordValue = document.querySelector('.edit__password').value; 54 | if (passwordValue.length > 0 && passwordValue.length < 8) return alert('Password Length must be greater than 7 characters.'); 55 | const password = passwordValue.length !== 0 ? passwordValue : undefined; 56 | 57 | const balance = document.querySelector('.edit__balance').value; 58 | if (isNaN(balance)) return alert('Wrong Value for Balance'); 59 | 60 | const updateObject = { 61 | email: document.querySelector('.edit__email').value, 62 | role: document.querySelector('.edit__role').value, 63 | balance: parseFloat(balance) 64 | } 65 | 66 | const updatedObject = password ? Object.assign(updateObject, {password}) : updateObject; 67 | 68 | try { 69 | const response = await axios.post(`/user/edit/${id}`, updatedObject); 70 | return response.data; 71 | } catch(e) { 72 | alert(e.response.data); 73 | } 74 | } 75 | 76 | /*------------------------ Event Listeners------------------------- */ 77 | 78 | //Show the number of users on page load 79 | window.addEventListener('DOMContentLoaded', () => { 80 | membersCount(elements.membersCount); 81 | }); 82 | 83 | //Adding event listeners on the `admin top buttons` - CLICK 84 | elements.adminTopBody.addEventListener('click', async(e) => { 85 | //Display members when `show all members` is clicked 86 | if (e.target === elements.showMembersButton) { 87 | //Empty username array before search 88 | allUsernames.splice(0,allUsernames.length); 89 | //Empty any content from display 90 | elements.adminOutputDisplay.innerHTML = ``; 91 | elements.holdButtonDiv.innerHTML = ``; 92 | //Get usernames 93 | const members = await membersUsername(allUsernames.length); 94 | //Show username if found 95 | if (members) { 96 | adminView.loadMoreButton(elements.holdButtonDiv,'Users'); 97 | members.forEach((member,i) => { 98 | allUsernames.push(member); 99 | adminView.displayUser(elements.adminOutputDisplay,member,i+1); 100 | }) 101 | } 102 | } 103 | }); 104 | 105 | //Adding event listeners on the `admin top buttons` - SUBMIT 106 | elements.adminTopBody.addEventListener('submit', async(e) => { 107 | if (e.target === elements.adminUserForm) { 108 | //Displaying user details when `edit member by username` form is submitted 109 | 110 | //0. Preventing Default Behavior 111 | e.preventDefault(); 112 | //1. Empty display area 113 | elements.adminOutputDisplay.innerHTML = ``; 114 | elements.holdButtonDiv.innerHTML = ``; 115 | //2.Get User Data 116 | const details = await getMemberDetails(elements.adminUserInput.value,'username'); 117 | //3. Displaying User Details if found 118 | if (details) { 119 | adminView.displayUserForm(elements.adminOutputDisplay,details); 120 | } 121 | 122 | } else if (e.target === elements.adminBetsForm) { 123 | //Display user bets when `show bets` form is submitted 124 | 125 | //0. Preventing Default Behavior 126 | e.preventDefault(); 127 | //0.1 Empty allBets array 128 | allBets.splice(0,allBets.length); 129 | //1. Empty display area 130 | elements.adminOutputDisplay.innerHTML = ``; 131 | elements.holdButtonDiv.innerHTML = ``; 132 | //2. Get User's Bet 133 | const bets = await getMemberBets(allBets.length,elements.adminBetsInput.value); 134 | //3. Displaying Bets if found 135 | if (bets) { 136 | adminView.loadMoreButton(elements.holdButtonDiv,'Bets'); 137 | bets.forEach(bet => { 138 | allBets.push(bet); 139 | adminView.displayBet(elements.adminOutputDisplay,bet); 140 | }); 141 | } 142 | } 143 | }); 144 | 145 | //Adding Event Listener on `admin bottom buttons` - CLICK 146 | elements.adminDisplayResult.addEventListener('click', async(e) => { 147 | //When `edit member` is clicked 148 | if (e.target.matches('.edit__member')) { 149 | //1. getting the id of member from data 150 | const id = e.target.closest('.user__span').dataset.id; 151 | //2. Get user profile with `id` 152 | const details = await getMemberDetails(id,'id'); 153 | //3. Display user profile if found 154 | if (details) { 155 | elements.adminOutputDisplay.innerHTML = ``; 156 | elements.holdButtonDiv.innerHTML = ``; 157 | adminView.displayUserForm(elements.adminOutputDisplay,details); 158 | } 159 | } 160 | //When `load more bets` is clicked 161 | else if (e.target.matches('.load__more-Bets')) { 162 | // Load More Bets 163 | const bets = await getMemberBets(allBets.length,elements.adminBetsInput.value); 164 | //If More Bets Found, Display them 165 | if (bets) { 166 | bets.forEach(bet => { 167 | allBets.push(bet); 168 | adminView.displayBet(elements.adminOutputDisplay,bet); 169 | }); 170 | } 171 | } 172 | //When `load more users` is clicked 173 | else if (e.target.matches('.load__more-Users')) { 174 | //Get more usernames 175 | const members = await membersUsername(allUsernames.length); 176 | //Show usernames if found 177 | if (members) { 178 | members.forEach((member,i) => { 179 | allUsernames.push(member); 180 | adminView.displayUser(elements.adminOutputDisplay,member,i+1); 181 | }) 182 | } 183 | } 184 | }) 185 | 186 | //Adding Event Listener on `admin bottom buttons` - SUBMIT 187 | elements.adminDisplayResult.addEventListener('submit', async(e) => { 188 | // Saving details of the user when `Save Changes` have been clicked 189 | if (e.target.matches('.user__edit-form')) { 190 | //0. Preventing Default Behavior 191 | e.preventDefault(); 192 | //1. Retrieving User ID 193 | const id = e.target.dataset.id; 194 | //2. Sending data to save 195 | const updatedDetails = await updateUser(id); 196 | //3. If details are updated, show new details 197 | if (updatedDetails) { 198 | elements.adminOutputDisplay.innerHTML = ``; 199 | elements.holdButtonDiv.innerHTML = ``; 200 | updatedDetails.forEach(update => elements.adminOutputDisplay.insertAdjacentHTML('beforeend', `${update}
`)); 201 | } 202 | } 203 | }); -------------------------------------------------------------------------------- /src/frontend/controllers/bet.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const elements = require('../views/base'); 3 | 4 | const createBet = async() => { 5 | //making HTTP request 6 | try { 7 | const response = await axios.post('/bet/create', { 8 | amount: elements.betBox.value, 9 | rolltype: elements.rollText.innerHTML, 10 | rollvalue: elements.rollBox.value, 11 | client: elements.clientBox.value 12 | }); 13 | return response.data; 14 | } catch(e) { 15 | elements.resultBox.innerHTML = e.response.data; 16 | } 17 | } 18 | 19 | //Retrieving Multiple Bets 20 | const retrieveBets = async(index) => { 21 | try { 22 | const response = await axios.get(`/user/bets?limit=5&skip=${index}`); 23 | return response.data; 24 | } catch(e) { 25 | localStorage.clear(); 26 | elements.myBetBox.innerHTML = e.response.data; 27 | } 28 | } 29 | 30 | //Retrieving Single Bet 31 | const retrieveBet = async(id) => { 32 | try { 33 | const response = await axios.get(`/user/bet/${id}`); 34 | return response.data; 35 | } catch(e) { 36 | alert(e.response.data); 37 | } 38 | } 39 | 40 | module.exports = { 41 | createBet, 42 | retrieveBets, 43 | retrieveBet 44 | } -------------------------------------------------------------------------------- /src/frontend/controllers/chat.js: -------------------------------------------------------------------------------- 1 | const socket = io(); 2 | const moment = require('moment'); 3 | const axios = require('axios'); 4 | const elements = require('../views/base'); 5 | const userView = require('../views/user'); 6 | const username = (localStorage.getItem('user')) ? JSON.parse(localStorage.getItem('user')).username : undefined; 7 | 8 | //Message Received Emit when message is typed 9 | elements.sendMessage.addEventListener('submit', e => { 10 | e.preventDefault(); 11 | //If user isn't logged in then show login form 12 | if (!localStorage.getItem('user')) { 13 | e.preventDefault(); 14 | return userView.loginFormDisplay(elements.overlayBox); 15 | } 16 | 17 | const message = elements.messageInput.value; 18 | elements.chatButton.setAttribute('disabled', 'disabled'); 19 | 20 | socket.emit('messageReceived', {message, username}, error => { 21 | elements.chatButton.removeAttribute('disabled'); 22 | elements.messageInput.value = ''; 23 | elements.messageInput.focus(); 24 | 25 | if (error) { 26 | return console.log(error); 27 | } 28 | }); 29 | }); 30 | 31 | //Socket on receiving that server validated message 32 | socket.on('message', async(message) => { 33 | try { 34 | const save = await axios.post('/chat/create', message); 35 | if (username) { 36 | displayMessage(message); 37 | } 38 | } catch(e) { 39 | if (e === 'authentication') return userView.loginFormDisplay(elements.overlayBox); 40 | alert(e); 41 | } 42 | 43 | }); 44 | 45 | //Displaying Message 46 | const displayMessage = (message) => { 47 | const markup = ` 48 |
49 |

${message.username} says.. 50 | ${moment(message.created).format('MMMM Do YYYY, h:mm:ss a')}

51 |

${message.text}

52 |
53 | `; 54 | 55 | elements.chatArea.insertAdjacentHTML('beforeend', markup); 56 | elements.chatArea.scrollTop = elements.chatArea.scrollHeight - elements.chatArea.clientHeight; 57 | } 58 | 59 | module.exports = displayMessage; -------------------------------------------------------------------------------- /src/frontend/controllers/index.js: -------------------------------------------------------------------------------- 1 | const elements = require('../views/base'); 2 | const userControls = require('./user'); 3 | const userView = require('../views/user'); 4 | const betControl = require('./bet'); 5 | const betView = require('../views/bet'); 6 | const valueControl = require('./values'); 7 | const messageControl = require('./message'); 8 | const clientSeedGenerator = require('../../utils/clientSeed'); 9 | 10 | //utility for chat 11 | require('./chat'); 12 | 13 | const myBetsHolder = []; 14 | 15 | /*-------------Event Listeners ----------------*/ 16 | 17 | //Event Listener on Page Load 18 | window.addEventListener('DOMContentLoaded', async() => { 19 | if (localStorage.getItem('user')) { 20 | //showing user details if logged in 21 | userView.loggedDetails(elements.tools,elements.serverBox); 22 | 23 | //loading last five bets of user 24 | const myBets = await betControl.retrieveBets(myBetsHolder.length); 25 | if (myBets) myBets.forEach(bet => myBetsHolder.push(bet)); 26 | myBetsHolder.forEach(bet => betView.displayBet(elements.myBetBox,bet,'beforeend')); 27 | } 28 | 29 | //showing random client seed 30 | clientSeedGenerator(elements.clientBox); 31 | 32 | //load messages 33 | await messageControl(); 34 | 35 | //Hash Change Event Needed to reload page to load cookie 36 | if (location.hash === '#reload') location.href = '/'; 37 | }); 38 | 39 | //Event Listeners for register, login, logout on tools div 40 | elements.tools.addEventListener('click', e => { 41 | if (e.target === elements.registerButton) { 42 | userView.registerFormDisplay(elements.overlayBox); 43 | } else if (e.target === elements.loginButton) { 44 | userView.loginFormDisplay(elements.overlayBox); 45 | } else if (e.target.matches('.logout__user')) userControls.logoutUser(); 46 | }); 47 | 48 | //Event Listener on elements of game body - INPUT 49 | elements.gameBody.addEventListener('input', (e) => { 50 | if (e.target === elements.slider) { 51 | //Changing values of inputs when slider is moved 52 | valueControl.sliderFunction(elements.slider); 53 | } else if (e.target === elements.betBox) { 54 | elements.profitBox.value = (elements.betBox.value*elements.payoutBox.value - elements.betBox.value).toFixed(8); 55 | } else if (e.target === elements.payoutBox) { 56 | //changing values when payout field is changed 57 | if (e.target.value > 50) e.target.value = 50; 58 | valueControl.inputFunction(e.target); 59 | } else if (e.target === elements.chanceBox) { 60 | if (e.target.value > 98) e.target.value = 98; 61 | valueControl.inputFunction(e.target); 62 | } 63 | }); 64 | 65 | //Event Listener on elements of game body - CLICK 66 | elements.gameBody.addEventListener('click', async(e) => { 67 | if (e.target === elements.shift || e.target === elements.rollBox) { 68 | //Toggle between 'roll under' and 'roll over' 69 | elements.slider.value = 100 - elements.slider.value; 70 | elements.rollBox.value = elements.slider.value; 71 | elements.rollText.innerHTML = elements.rollText.innerHTML === 'under' ? 'over' : 'under'; 72 | } else if (e.target === elements.rollButton) { 73 | e.target.setAttribute('disabled', 'disabled'); 74 | //Checking if user is logged in 75 | if (!localStorage.getItem('user')) return userView.loginFormDisplay(elements.overlayBox); 76 | //Creating Bet 77 | const betObject = await betControl.createBet(); 78 | if (betObject) { 79 | const {user,bet} = betObject; 80 | //Displaying Bet Result 81 | betView.displayResult(elements.resultBox,bet.display.won,bet.display.result); 82 | betView.displayBet(elements.myBetBox,bet,'afterbegin'); 83 | //Updating front-end user's details 84 | localStorage.setItem('user', JSON.stringify(user)); 85 | userView.loggedDetails(elements.tools,elements.serverBox) 86 | } 87 | e.target.removeAttribute('disabled'); 88 | } else if (e.target === elements.loadBets) { 89 | //Load next 10 bets 90 | const myNewBets = await betControl.retrieveBets(myBetsHolder.length); 91 | myNewBets.forEach(bet => myBetsHolder.push(bet)); 92 | myNewBets.forEach(bet => betView.displayBet(elements.myBetBox,bet,'beforeend')); 93 | } else if (e.target.matches('.bet__verify')) { 94 | //Bet Verification Data Display - Hash and Seeds 95 | const _id = e.target.closest('.bet').dataset.id; 96 | let selectedBet = myBetsHolder.find(bet => bet._id === _id); 97 | if (!selectedBet) selectedBet = await betControl.retrieveBet(_id); 98 | betView.displayVerify(elements.overlayBox,selectedBet); 99 | 100 | //Close Provably Fair Window 101 | document.querySelector('.close__verify').onclick = () => { 102 | document.querySelector('.verify__data').remove(); 103 | document.querySelector('.black__back').remove(); 104 | } 105 | } 106 | }); 107 | 108 | //Event Listener on Login and Register Button 109 | elements.overlayBox.addEventListener('click', e => { 110 | if (e.target.matches('.login__form-button')) { 111 | e.preventDefault(); 112 | document.querySelector('.login__form-button').setAttribute('disabled', 'disabled'); 113 | userControls.loginUser(); 114 | } else if (e.target.matches('.register__form-button')) { 115 | e.preventDefault(); 116 | document.querySelector('.register__form-button').setAttribute('disabled', 'disabled'); 117 | userControls.registerUser(); 118 | } else if (e.target.matches('.register__instead')) { 119 | //Showing Register Form if someone clicked 'Or Register' on login form 120 | userView.registerFormDisplay(elements.overlayBox); 121 | } 122 | }); 123 | -------------------------------------------------------------------------------- /src/frontend/controllers/message.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | const displayMessage = require('./chat'); 3 | 4 | //Displaying Messages to user on load; 5 | module.exports = async() => { 6 | try { 7 | const messages = await axios.get('/chat/messages'); 8 | messages.data.forEach(message => displayMessage(message)); 9 | } catch(e) { 10 | alert(e.response.data); 11 | } 12 | } -------------------------------------------------------------------------------- /src/frontend/controllers/user.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | //trying to login function 4 | const loginUser = async() => { 5 | let response; 6 | try { 7 | response = await axios.post('/user/login', { 8 | username: document.getElementById('username').value, 9 | password: document.getElementById('password').value, 10 | }); 11 | 12 | localStorage.setItem('user', JSON.stringify(response.data)); 13 | window.location.hash = 'reload'; 14 | location.reload(); 15 | 16 | } catch(e) { 17 | document.querySelector('.login__error').innerHTML = e.response.data; 18 | document.querySelector('.login__form-button').removeAttribute('disabled'); 19 | } 20 | } 21 | 22 | //trying to Logout function 23 | const logoutUser = async() => { 24 | let response; 25 | try { 26 | response = await axios.post('/user/logout'); 27 | 28 | localStorage.clear(); 29 | location.reload(); 30 | } catch { 31 | alert('Something Went Wrong. Please refresh the page.'); 32 | document.querySelector('.logout__user').removeAttribute('disabled'); 33 | } 34 | } 35 | 36 | //try to Register function 37 | const registerUser = async () => { 38 | let response; 39 | try { 40 | response = await axios.post('/user/signup', { 41 | username: document.getElementById('username').value, 42 | password: document.getElementById('password').value, 43 | email: document.getElementById('email').value 44 | }); 45 | 46 | localStorage.setItem('user', JSON.stringify(response.data)); 47 | window.location.hash = 'reload'; 48 | location.reload(); 49 | 50 | } catch(e) { 51 | document.querySelector('.register__error').innerHTML = e.response.data.join('
'); 52 | document.querySelector('.register__form-button').removeAttribute('disabled'); 53 | } 54 | } 55 | 56 | module.exports = { 57 | loginUser, 58 | logoutUser, 59 | registerUser 60 | } -------------------------------------------------------------------------------- /src/frontend/controllers/values.js: -------------------------------------------------------------------------------- 1 | const elements = require('../views/base'); 2 | 3 | //Inserting value in boxes when slider changes 4 | const sliderFunction = function(element) { 5 | const value = parseInt(element.value); 6 | const payoutValue = elements.rollText.innerHTML === 'under' ? (100/value).toFixed(4) : (100/(100-value)).toFixed(4); 7 | const chanceValue = elements.rollText.innerHTML === 'under' ? value : 100 - value; 8 | const profitValue = (elements.betBox.value*payoutValue - elements.betBox.value).toFixed(8); 9 | 10 | elements.rollBox.value = value; 11 | elements.chanceBox.value = chanceValue; 12 | elements.payoutBox.value = payoutValue; 13 | elements.profitBox.value = profitValue; 14 | } 15 | 16 | //Inserting value in boxes when either 'payout' or 'win chance changes' 17 | const inputFunction = function(element) { 18 | const value = element.value; 19 | 20 | if (element === elements.payoutBox) { 21 | var rollValue = elements.rollText.innerHTML === 'under' ? (100/value).toFixed(4) : (100 - (100/value)).toFixed(4); 22 | } else { 23 | var rollValue = elements.rollText.innerHTML === 'under' ? value : 100 - value; 24 | } 25 | 26 | elements.rollBox.value = rollValue; 27 | rollFunction(element); 28 | } 29 | 30 | const rollFunction = function(element) { 31 | const value = elements.rollBox.value; 32 | const payoutValue = elements.rollText.innerHTML === 'under' ? (100/value).toFixed(4) : (100/(100-value)).toFixed(4); 33 | const chanceValue = elements.rollText.innerHTML === 'under' ? value : 100 - value; 34 | const profitValue = (elements.betBox.value*payoutValue - elements.betBox.value).toFixed(8); 35 | 36 | elements.slider.value = value; 37 | elements.profitBox.value = profitValue; 38 | 39 | if (element === elements.payoutBox) { 40 | elements.chanceBox.value = chanceValue; 41 | } else if (element === elements.chanceBox) { 42 | elements.payoutBox.value = payoutValue; 43 | } else { 44 | elements.payoutBox.value = payoutValue; 45 | elements.chanceBox.value = chanceValue; 46 | } 47 | } 48 | 49 | module.exports = { 50 | sliderFunction, 51 | inputFunction, 52 | rollFunction 53 | } -------------------------------------------------------------------------------- /src/frontend/views/admin.js: -------------------------------------------------------------------------------- 1 | //Display each user in separate line 2 | const displayUser = (element,user,i) => { 3 | const markup = ` 4 | 5 | ${i}. ${user.username} 6 | 7 |
8 | `; 9 | element.insertAdjacentHTML('beforeend',markup); 10 | } 11 | 12 | //Display details of single user in form 13 | const displayUserForm = (element,user) => { 14 | const markup = ` 15 |
16 | Username:
17 | Email: 18 | Password:
19 | Role: 20 | Balance:
21 | 22 |
23 | `; 24 | element.innerHTML = markup; 25 | } 26 | 27 | //Display each bet in separate line 28 | const displayBet = (element,bet) => { 29 | const markup = ` 30 | 31 | ${bet.choice} | 32 | ${bet.result} | 33 | ${bet.won} | 34 | ${(bet.payout).toFixed(8)} 35 |
36 | `; 37 | element.insertAdjacentHTML('beforeend',markup); 38 | } 39 | 40 | const loadMoreButton = (element,type) => { 41 | const markup = ` 42 | 43 | `; 44 | element.insertAdjacentHTML('beforeend', markup); 45 | } 46 | 47 | module.exports = { 48 | displayUser, 49 | displayUserForm, 50 | displayBet, 51 | loadMoreButton 52 | } -------------------------------------------------------------------------------- /src/frontend/views/base.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | slider: document.querySelector('.slider'), 3 | rollBox: document.querySelector('.roll__box'), 4 | payoutBox: document.querySelector('.payout__box'), 5 | chanceBox: document.querySelector('.chance__box'), 6 | shift: document.querySelector('.shift'), 7 | rollText: document.querySelector('.roll__text'), 8 | profitBox: document.querySelector('.profit__box'), 9 | betBox: document.querySelector('.bet__box'), 10 | registerButton: document.querySelector('.register__button'), 11 | loginButton: document.querySelector('.login__button'), 12 | overlayBox: document.querySelector('.overlay__box'), 13 | tools: document.querySelector('.tools'), 14 | serverBox: document.querySelector('.server__box'), 15 | clientBox: document.querySelector('.client__box'), 16 | gameBody: document.querySelector('.game__body'), 17 | rollButton: document.querySelector('.roll__button'), 18 | resultBox: document.querySelector('.result__box'), 19 | myBetBox: document.querySelector('.my__bets'), 20 | loadBets: document.querySelector('.load__bets'), 21 | sendMessage: document.querySelector('.send__message'), 22 | messageInput: document.querySelector('.message__input'), 23 | chatButton: document.querySelector('.send__button'), 24 | chatArea: document.querySelector('.chat__messages'), 25 | membersCount: document.querySelector('.members__count'), 26 | adminTopBody: document.querySelector('.admin__body-top'), 27 | showMembersButton: document.querySelector('.show__members-button'), 28 | adminDisplayResult: document.querySelector('.display__result'), 29 | adminUserForm: document.querySelector('.member__search-form'), 30 | adminBetsForm: document.querySelector('.bet__search-form'), 31 | adminUserInput: document.querySelector('.member__search-input'), 32 | adminBetsInput: document.querySelector('.bet__search-input'), 33 | holdButtonDiv: document.querySelector('.hold__button'), 34 | adminOutputDisplay: document.querySelector('.output__display') 35 | } -------------------------------------------------------------------------------- /src/frontend/views/bet.js: -------------------------------------------------------------------------------- 1 | const displayResult = (element,isWon,result) => { 2 | const markup = ` 3 | Roll Result: ${result}. You ${isWon}!! 4 | `; 5 | element.innerHTML = markup; 6 | } 7 | 8 | const displayBet = (element,bet,where) => { 9 | const markup = ` 10 | 11 | ${bet.display.choice} | 12 | ${bet.display.result} | 13 | ${bet.display.won} | 14 | ${(bet.display.payout).toFixed(8)} 15 | Verify
16 |
17 | `; 18 | 19 | element.insertAdjacentHTML(where,markup); 20 | } 21 | 22 | const displayVerify = (element,bet) => { 23 | const markup = ` 24 |
25 |

Provably Fair Data

26 |
Roll Result: ${bet.display.result}
27 |
Client Seed: ${bet.client}
28 |
Server Seed: ${bet.seed}
29 |
Server Seed Hash: ${bet.seedHash}
30 |
Hmac-512 Hash: ${bet.hash}
31 | 32 |
33 |
34 | `; 35 | element.innerHTML = markup; 36 | } 37 | 38 | module.exports = { 39 | displayBet, 40 | displayVerify, 41 | displayResult 42 | } -------------------------------------------------------------------------------- /src/frontend/views/user.js: -------------------------------------------------------------------------------- 1 | //Display Login Form 2 | const loginFormDisplay = (element) => { 3 | const markup = ` 4 | 15 |
16 | `; 17 | element.innerHTML = markup; 18 | } 19 | 20 | //Display Register Form 21 | const registerFormDisplay = (element) => { 22 | const markup = ` 23 |
24 |
25 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 |
36 | `; 37 | element.innerHTML = markup; 38 | } 39 | 40 | //Display Buttons when user is logged in 41 | const loggedDetails = (element,element2) => { 42 | const user = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : undefined; 43 | if (user) { 44 | var {username,balance,seedHash} = user; 45 | } 46 | 47 | const markup = ` 48 | 49 | 50 | 51 | `; 52 | 53 | element.innerHTML = markup; 54 | element2.value = seedHash; 55 | } 56 | 57 | module.exports = { 58 | loginFormDisplay, 59 | registerFormDisplay, 60 | loggedDetails 61 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cookieParser = require('cookie-parser'); 3 | const socketio = require('socket.io'); 4 | const Filter = require('bad-words'); 5 | const path = require('path'); 6 | const http = require('http'); 7 | const userRouter = require('./routers/user'); 8 | const betRouter = require('./routers/bet'); 9 | const chatRouter = require('./routers/chat'); 10 | const adminRouter = require('./routers/admin'); 11 | const chatAuth = require('./middleware/chatAuth'); 12 | const generateMessage = require('./utils/chat'); 13 | require('./db/mongoose'); 14 | 15 | //setting up socket 16 | const app = express(); 17 | const server = http.createServer(app); 18 | const io = socketio(server); 19 | 20 | app.use(express.json()); 21 | app.use(express.urlencoded({extended: false})); 22 | app.use(cookieParser()); 23 | app.use(userRouter); 24 | app.use(betRouter); 25 | app.use(chatRouter); 26 | app.use(adminRouter); 27 | app.use(express.static(path.join(__dirname, '../public'))); 28 | 29 | //Chat setup - Socket.io 30 | io.on('connection', socket => { 31 | socket.on('messageReceived', async({message, username}, callback) => { 32 | const user = await chatAuth(socket); 33 | if (!user) return callback('authentication'); 34 | 35 | const filter = new Filter(); 36 | if (filter.isProfane(message)) { 37 | return callback('Profanity is not allowed'); 38 | } 39 | io.emit('message', generateMessage(username, message)); 40 | callback(); 41 | }); 42 | }); 43 | 44 | server.listen(process.env.PORT, () => { 45 | console.log(`Listening on ${process.env.PORT}`); 46 | }); 47 | -------------------------------------------------------------------------------- /src/middleware/adminAuth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const User = require('../models/user'); 3 | 4 | const authentication = async (req,res,next) => { 5 | try { 6 | const token = req.cookies['auth_token']; 7 | const decode = jwt.verify(token, process.env.JWT_TOKEN); 8 | const user = await User.findOne({_id:decode._id, 'tokens.token': token}); 9 | 10 | //If member's role is not `admin`, return error 11 | if (!user || user.role !== 'admin') { 12 | throw new Error; 13 | } 14 | 15 | req.user = user; 16 | req.token = token; 17 | next(); 18 | } catch(e) { 19 | return res.status(400).send('Access Denied'); 20 | } 21 | } 22 | 23 | module.exports = authentication; -------------------------------------------------------------------------------- /src/middleware/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const User = require('../models/user'); 3 | 4 | const authentication = async (req,res,next) => { 5 | try { 6 | const token = req.cookies['auth_token']; 7 | const decode = jwt.verify(token, process.env.JWT_TOKEN); 8 | const user = await User.findOne({_id:decode._id, 'tokens.token': token}); 9 | 10 | if (!user) { 11 | throw new Error; 12 | } 13 | 14 | req.user = user; 15 | req.token = token; 16 | next(); 17 | } catch(e) { 18 | return res.status(400).send('Authentication Error'); 19 | } 20 | } 21 | 22 | module.exports = authentication; -------------------------------------------------------------------------------- /src/middleware/chatAuth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const User = require('../models/user'); 3 | 4 | const authentication = async(socket) => { 5 | try { 6 | const token = socket.handshake.headers.cookie.split(';')[0].split('=')[1]; 7 | const decode = jwt.verify(token, process.env.JWT_TOKEN); 8 | const user = await User.findOne({_id:decode._id, 'tokens.token': token}); 9 | return user; 10 | } catch { 11 | return undefined; 12 | } 13 | } 14 | 15 | module.exports = authentication; -------------------------------------------------------------------------------- /src/models/bet.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const betSchema = new mongoose.Schema({ 4 | player: { 5 | type: mongoose.ObjectId, 6 | required: true, 7 | ref: 'User' 8 | }, 9 | amount: { 10 | type: Number, 11 | required: true 12 | }, 13 | range: { 14 | start: { 15 | type: Number, 16 | required: true 17 | }, 18 | end: { 19 | type: Number, 20 | required: true 21 | } 22 | }, 23 | hash: { 24 | type: String, 25 | required: true 26 | }, 27 | seedHash: { 28 | type: String, 29 | required: true 30 | }, 31 | seed: { 32 | type: String, 33 | required: true 34 | }, 35 | client: { 36 | type: String, 37 | required: true 38 | }, 39 | display: { 40 | choice: { 41 | type: String, 42 | required: true 43 | }, 44 | result: { 45 | type: Number, 46 | required: true 47 | }, 48 | won: { 49 | type: String, 50 | required: true 51 | }, 52 | payout: { 53 | type: Number, 54 | required: true 55 | } 56 | } 57 | }, { 58 | timestamps: true 59 | }); 60 | 61 | //Creating Bet Model 62 | const Bet = mongoose.model('Bet', betSchema); 63 | 64 | module.exports = Bet; -------------------------------------------------------------------------------- /src/models/chat.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const chatSchema = new mongoose.Schema({ 4 | username: { 5 | type: String, 6 | required: true 7 | }, 8 | text: { 9 | type: String, 10 | required: true 11 | }, 12 | created: { 13 | type: Number, 14 | required: true 15 | } 16 | },{ 17 | timestamps: true 18 | }); 19 | 20 | 21 | //Creating Chat Model 22 | const Chat = mongoose.model('Chat', chatSchema); 23 | 24 | module.exports = Chat; -------------------------------------------------------------------------------- /src/models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const bcrypt = require('bcryptjs'); 3 | const validator = require('validator'); 4 | const jwt = require('jsonwebtoken'); 5 | 6 | //User Schema 7 | const userSchema = new mongoose.Schema( { 8 | username: { 9 | type: String, 10 | required: true, 11 | unique: true, 12 | trim: true, 13 | immutable: true, 14 | minlength: [4, 'Username should be of minimum 4 characters'], 15 | validate(value) { 16 | if (value.includes(' ')) { 17 | throw new Error('Spaces are not allowed in username'); 18 | } 19 | } 20 | }, 21 | password: { 22 | type: String, 23 | required: true, 24 | validate(value) { 25 | if (value.length < 8) { 26 | throw new Error('Password must contain minimum 8 characters'); 27 | } 28 | if (value.includes('password')) { 29 | throw new Error('Password should not include the term password') 30 | } 31 | } 32 | }, 33 | email: { 34 | unique: true, 35 | type: String, 36 | required: true, 37 | lowercase: true, 38 | validate(value) { 39 | if (!validator.isEmail(value)) { 40 | throw new Error('Invalid Email'); 41 | } 42 | } 43 | }, 44 | balance: { 45 | type: Number, 46 | default: 500 47 | }, 48 | role: { 49 | type: String, 50 | default: 'member', 51 | enum: ['admin', 'member'] 52 | }, 53 | seedHash: { 54 | type: String, 55 | required: true 56 | }, 57 | seed: { 58 | type: String, 59 | required: true 60 | }, 61 | tokens: [{ 62 | token: { 63 | type: String, 64 | required: true 65 | } 66 | }] 67 | }, { 68 | timestamps: true 69 | }); 70 | 71 | //Hashing passwords 72 | userSchema.pre('save', async function(next) { 73 | const user = this; 74 | if (user.isModified('password')) { 75 | user.password = await bcrypt.hash(user.password, 8); 76 | } 77 | }); 78 | 79 | //Sending custom errors when email or username exists 80 | userSchema.post('save', function(error, doc, next) { 81 | if (error.name === 'MongoError' && error.code === 11000) { 82 | next(new Error('username or email id already exists')); 83 | } else { 84 | next(error); 85 | } 86 | }); 87 | 88 | //Authentication tokens for users 89 | userSchema.methods.createAuthToken = async function() { 90 | const user = this; 91 | const token = jwt.sign({_id: user._id.toString()}, process.env.JWT_TOKEN); 92 | user.tokens = user.tokens.concat({token}); 93 | await user.save(); 94 | return token; 95 | } 96 | 97 | //Login Endpoint for Users 98 | userSchema.statics.loginWithCredentials = async(username, password) => { 99 | const user = await User.findOne({username}); 100 | if (!user) { 101 | throw new Error('Invalid Login Details'); 102 | } 103 | 104 | const matched = await bcrypt.compare(password, user.password); 105 | 106 | if (!matched) { 107 | throw new Error('Invalid Login Details'); 108 | } 109 | return user; 110 | } 111 | 112 | //Hiding private data of the user from response 113 | userSchema.methods.toJSON = function() { 114 | const user = this; 115 | const userObject = user.toObject(); 116 | 117 | delete userObject.password; 118 | delete userObject.tokens; 119 | delete userObject.seed; 120 | 121 | return userObject; 122 | } 123 | 124 | //Creating virtual property to retrieve bets made by the user 125 | userSchema.virtual('bets', { 126 | ref: 'Bet', 127 | localField: '_id', 128 | foreignField: 'player' 129 | }); 130 | 131 | //Creating User Model 132 | const User = mongoose.model('User', userSchema); 133 | 134 | module.exports = User; -------------------------------------------------------------------------------- /src/private/admin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Admin Panel 7 | 8 | 9 | 10 |

Admin Panel

11 |
12 |
13 |

Total Members:

14 | 15 |
16 |
17 |

Edit Member By Username

18 |
19 | 20 | 21 |
22 |
23 |
24 |

Show Bets

25 |
26 | 27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | 40 | 41 | -------------------------------------------------------------------------------- /src/routers/admin.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | 4 | const User = require('../models/user'); 5 | const adminAuth = require('../middleware/adminAuth'); 6 | 7 | const router = express.Router(); 8 | 9 | //showing admin panel to only admin 10 | router.get('/admin', adminAuth, (req,res) => { 11 | res.sendFile(path.resolve(__dirname, '../private/admin.html')) 12 | }); 13 | 14 | 15 | //Return the total number of members registered on website 16 | router.get('/users/all', adminAuth, async(req,res) => { 17 | try { 18 | const count = await User.countDocuments(); 19 | res.send(count.toString()); 20 | } catch { 21 | res.status(500).send('NaN'); 22 | } 23 | }); 24 | 25 | //Return the username of 20 members in ascending order 26 | router.get('/users/details', adminAuth, async(req,res) => { 27 | try { 28 | const users = await User.find({}, '_id username').sort({username: 1}).limit(20) 29 | .skip(parseInt(req.query.skip)); 30 | res.send(users); 31 | } catch(e) { 32 | res.status(500).send('Failed to load members'); 33 | } 34 | }); 35 | 36 | //Show the data of a particular member - Searching with Id 37 | router.get('/user/getinfo/id/:id', adminAuth, async(req,res) => { 38 | const id = req.params.id; 39 | try { 40 | const user = await User.findById(id); 41 | if (!user) return res.status(400).send('User not found'); 42 | res.send(user); 43 | } catch { 44 | res.status(500).send('Failed to load details'); 45 | } 46 | }); 47 | 48 | //Show the data of a particular member - Searching with username 49 | router.get('/user/getinfo/username/:username', adminAuth, async(req,res) => { 50 | const username = req.params.username; 51 | try { 52 | const user = await User.findOne({username}); 53 | if (!user) return res.status(400).send('User not found'); 54 | res.send(user); 55 | } catch { 56 | res.status(500).send('Failed to load details'); 57 | } 58 | }); 59 | 60 | //Edit the details of the member 61 | router.post('/user/edit/:id', adminAuth, async(req,res) => { 62 | const updates = Object.keys(req.body); 63 | const AllowedUpdates = ['balance','password','email','role']; 64 | const isValidOperation = updates.every(update => AllowedUpdates.includes(update)); 65 | const changeStatus = []; 66 | 67 | if (!isValidOperation) { 68 | return res.status(400).send('Invalid Changes'); 69 | } 70 | 71 | try { 72 | const user = await User.findById(req.params.id); 73 | if (!user) return res.status(400).send('User does not exists'); 74 | 75 | updates.forEach(update => { 76 | if (user[update] !== req.body[update]) { 77 | user[update] = req.body[update]; 78 | changeStatus.push(`${update} has been changed!`); 79 | } else { 80 | changeStatus.push(`${update} remains same!`); 81 | } 82 | }); 83 | 84 | await user.save(); 85 | res.send(changeStatus); 86 | } catch(e) { 87 | res.status(500).send('Failed to change details'); 88 | } 89 | }); 90 | 91 | //Displaying bets made by the user 92 | router.get('/user/allbets/:username', adminAuth, async(req,res) => { 93 | const username = req.params.username; 94 | try { 95 | const user = await User.findOne({username}); 96 | if (!user) return res.status(400).send('User not found'); 97 | 98 | await user.populate({ 99 | path: 'bets', 100 | options: { 101 | limit: 20, 102 | skip: parseInt(req.query.skip), 103 | } 104 | }).execPopulate(); 105 | 106 | const bets = user.bets.map(bet => bet.display); 107 | 108 | res.send(bets); 109 | } catch(e) { 110 | res.status(500).send('Failed to load bets'); 111 | } 112 | }); 113 | 114 | module.exports = router; 115 | -------------------------------------------------------------------------------- /src/routers/bet.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const NP = require('number-precision'); 3 | 4 | const Bet = require('../models/bet'); 5 | const User = require('../models/user'); 6 | const authentication = require('../middleware/auth'); 7 | const createServerSeed = require('../utils/serverSeed'); 8 | const findResult = require('../utils/pf'); 9 | 10 | const router = express.Router(); 11 | 12 | router.post('/bet/create', authentication, async(req,res) => { 13 | let start, end, payout; 14 | 15 | //taking inputs from user 16 | const {amount,rollvalue,rolltype,client} = req.body; 17 | 18 | //validating inputs - amount and client seed 19 | if (amount > req.user.balance || client.length > 16) return res.status(400).send('Not Enough Money or Wrong Client Seed'); 20 | 21 | try { 22 | //checking if user bet roll under or over 23 | if (rolltype === 'under') { 24 | end = rollvalue; 25 | start = 0; 26 | payout = (100/rollvalue).toFixed(4); 27 | } else if (rolltype === 'over') { 28 | end = 100; 29 | start = rollvalue; 30 | payout = (100/(100-rollvalue)).toFixed(4); 31 | } else return res.status(400).send('Bad Request'); 32 | 33 | //searching for current user seed 34 | const {seed,seedHash} = await User.findById(req.user._id); 35 | 36 | //using provably fair script to find result 37 | const {hash, number} = findResult(client,seed); 38 | 39 | //checking if user won or lost 40 | const isWin = (number > parseInt(start) && number < parseInt(end)); 41 | 42 | //if win then add amount else deduct 43 | const modAmount = NP.times(amount,payout); 44 | const winAmount = NP.minus(modAmount,amount) 45 | req.user.balance = isWin ? NP.plus(req.user.balance,winAmount) : NP.minus(req.user.balance,amount); 46 | 47 | //creating new server seed for user 48 | const newServerSeeds = createServerSeed(); 49 | req.user.seed = newServerSeeds.seed; 50 | req.user.seedHash = newServerSeeds.seedHash; 51 | 52 | //creating bet 53 | const bet = new Bet({ 54 | player: req.user._id, 55 | amount, 56 | range: {start,end}, 57 | hash, 58 | seed, seedHash, client, 59 | display: { 60 | choice: rollvalue+' '+rolltype, 61 | result: number, 62 | won: isWin ? 'won' : 'lost', 63 | payout: isWin ? winAmount : `-${amount}` 64 | } 65 | }); 66 | 67 | //saving bet and user in database 68 | await bet.save(); 69 | await req.user.save(); 70 | 71 | //rendering result to frontend 72 | res.send({bet, user: req.user}); 73 | 74 | } catch(e) { 75 | res.status(500).send('Something Went Wrong!'); 76 | } 77 | }); 78 | 79 | //Retrieve newly created single bet 80 | router.get('/user/bet/:id',authentication, async(req,res) => { 81 | try { 82 | const _id = req.params.id; 83 | const bet = await Bet.findOne({_id, player: req.user._id}); 84 | 85 | if (!bet) return res.status(400).send('Bad Request'); 86 | res.send(bet); 87 | } catch { 88 | res.status(500).send('Failed to load bet'); 89 | } 90 | }); 91 | 92 | 93 | module.exports = router; 94 | 95 | -------------------------------------------------------------------------------- /src/routers/chat.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const Chat = require('../models/chat'); 4 | const authentication = require('../middleware/auth'); 5 | 6 | const router = express.Router(); 7 | 8 | router.post('/chat/create', authentication, async(req,res) => { 9 | const {text,created} = req.body; 10 | try { 11 | const chat = new Chat({ 12 | text, 13 | username: req.user.username, 14 | created 15 | }) 16 | await chat.save(); 17 | res.send(chat); 18 | } catch(e) { 19 | res.status(500).send(e+'Something Went Wrong!'); 20 | } 21 | }); 22 | 23 | router.get('/chat/messages', async(req,res) => { 24 | try { 25 | const messages = await Chat.find({}, null, {sort: {createdAt: -1}}).limit(20); 26 | res.send(messages.reverse()); 27 | } catch(e) { 28 | res.status(500).send('Failed to load messages'); 29 | } 30 | 31 | }); 32 | 33 | module.exports = router; 34 | 35 | -------------------------------------------------------------------------------- /src/routers/user.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const User = require('../models/user'); 4 | const authentication = require('../middleware/auth'); 5 | const createServerSeed = require('../utils/serverSeed'); 6 | 7 | const router = express.Router(); 8 | 9 | //router to signup user 10 | router.post('/user/signup', async(req, res) => { 11 | const {username,email,password} = req.body; 12 | const {seed,seedHash} = createServerSeed(); 13 | const user = new User({ 14 | username, 15 | email, 16 | password, 17 | seed, 18 | seedHash 19 | }); 20 | try { 21 | await user.save(); 22 | const token = await user.createAuthToken(); 23 | res.cookie('auth_token', token, {httpOnly: true}); 24 | res.status(201).send(user); 25 | } catch(e) { 26 | const errorMsg = []; 27 | if (e.errors) { 28 | Object.values(e.errors).forEach( 29 | error => errorMsg.push(error.message) 30 | ); 31 | res.status(400).send(errorMsg); 32 | } else { 33 | res.status(400).send([e.message]); 34 | } 35 | } 36 | }); 37 | 38 | //router to login user 39 | router.post('/user/login', async(req, res) => { 40 | try { 41 | const user = await User.loginWithCredentials(req.body.username, req.body.password); 42 | const token = await user.createAuthToken(); 43 | res.cookie('auth_token', token, {httpOnly: true}); 44 | 45 | res.send(user); 46 | } catch(e) { 47 | res.status(400).send(e.message); 48 | } 49 | }); 50 | 51 | //router to logout user 52 | router.post('/user/logout', authentication, async(req,res) => { 53 | try { 54 | req.user.tokens = req.user.tokens.filter(token => token.token !== req.token); 55 | 56 | await req.user.save(); 57 | res.send(); 58 | } catch(e) { 59 | res.status(500).send(e); 60 | } 61 | }); 62 | 63 | //router to retrieve all bets made by the user 64 | router.get('/user/bets', authentication, async(req,res) => { 65 | try { 66 | await req.user.populate({ 67 | path: 'bets', 68 | options: { 69 | limit: parseInt(req.query.limit), 70 | skip: parseInt(req.query.skip), 71 | sort: { 72 | createdAt: -1 73 | } 74 | } 75 | }).execPopulate(); 76 | res.send(req.user.bets); 77 | } catch { 78 | res.status(500).send('Failed to load bets'); 79 | } 80 | }); 81 | 82 | 83 | module.exports = router; 84 | -------------------------------------------------------------------------------- /src/utils/chat.js: -------------------------------------------------------------------------------- 1 | module.exports = (username, message) => { 2 | return { 3 | username, 4 | text: message, 5 | created: new Date().getTime() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/clientSeed.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | module.exports = (field) => { 4 | let length = Math.floor(Math.random()*8); 5 | if (length < 4) length = 4; 6 | 7 | const clientSeed = crypto.randomBytes(length).toString('hex'); 8 | 9 | field.value = clientSeed; 10 | } -------------------------------------------------------------------------------- /src/utils/pf.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | module.exports = (client,server) => { 4 | const hash = crypto.createHmac('sha512', server).update(client).digest('hex'); 5 | let index = 0; 6 | let lucky = parseInt(hash.substring(index*5,index*5+5),16); 7 | 8 | while(lucky >= Math.pow(10,6)) { 9 | index += 1; 10 | lucky = parseInt(hash.substring(index*5,index*5+5),16); 11 | 12 | if (index*5+5 >129) { 13 | lucky = 9999; 14 | break; 15 | } 16 | } 17 | return {hash, number: (lucky%10000)/100}; 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/serverSeed.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | module.exports = () => { 4 | const seed = crypto.randomBytes(24).toString('hex'); 5 | const seedHash = crypto.createHash('sha256').update(seed).digest('hex'); 6 | 7 | return { 8 | seed, seedHash 9 | } 10 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: { 5 | index: './src/frontend/controllers/index.js', 6 | admin: './src/frontend/controllers/admin.js' 7 | }, 8 | output: { 9 | path: path.resolve(__dirname, './public'), 10 | filename: 'js/[name].js' 11 | } 12 | } --------------------------------------------------------------------------------