├── 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 | Login
15 |
16 |
17 |
18 |
51 |
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 `;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 |
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 |
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 |
22 |
23 |
24 |
Show Bets
25 |
29 |
30 |
31 |
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 | }
--------------------------------------------------------------------------------