· 4 years ago · May 05, 2021, 12:22 PM
1// Credit DB model
2"use strict";
3const { Model } = require("sequelize");
4module.exports = (sequelize, DataTypes) => {
5 class Credit extends Model {
6 /**
7 * Helper method for defining associations.
8 * This method is not a part of Sequelize lifecycle.
9 * The `models/index` file will call this method automatically.
10 */
11 static associate(models) {}
12 }
13 Credit.init(
14 {
15 address: {
16 type: DataTypes.UUID,
17 defaultValue: DataTypes.UUIDV1,
18 },
19 amount: DataTypes.DOUBLE,
20 numberOfMonth: DataTypes.INTEGER,
21 amountWithPercent: DataTypes.DOUBLE,
22 accountId: DataTypes.INTEGER,
23 percent: DataTypes.DOUBLE,
24 timeEnd: DataTypes.DATE,
25 amountReceived: DataTypes.DOUBLE,
26 amountRemained: DataTypes.DOUBLE,
27 },
28 {
29 sequelize,
30 modelName: "Credit",
31 }
32 );
33 return Credit;
34};
35
36// Authenticator middleware
37module.exports = (jwt, wrap, config, errors) =>
38 wrap(async (req, res, next) => {
39 if (req._parsedUrl.pathname.indexOf("auth") !== -1) {
40 next();
41 } else if (req.header("Authorization")) {
42 let auth = req.header("Authorization").split(" ")[1];
43 jwt.verify(auth, config.cookie.key, (err, decoded) => {
44 if (err) {
45 res.status(errors.forbidden.status).send(errors.forbidden);
46 } else {
47 req.user = decoded;
48 next();
49 }
50 });
51 } else {
52 res.status(errors.unauthorized.status).send(errors.unauthorized);
53 }
54 });
55// Credit Controller
56const CrudController = require("../crud/crud.controller");
57
58class CreditController extends CrudController {
59 constructor(creditService) {
60 super(creditService);
61 this.getAll = this.getAll.bind(this);
62 this.payFee = this.payFee.bind(this);
63
64 this.routes["/:id"] = [
65 {
66 method: "get",
67 cb: this.get,
68 },
69 {
70 method: "delete",
71 cb: this.delete,
72 },
73 ];
74 this.routes["/"] = [
75 {
76 method: "get",
77 cb: this.getAll,
78 },
79 {
80 method: "post",
81 cb: this.create,
82 },
83 {
84 method: "patch",
85 cb: this.update,
86 },
87 ];
88 this.routes["/payfee"] = [
89 {
90 method: "post",
91 cb: this.payFee,
92 },
93 ];
94 this.registerRoutes();
95 }
96
97 async update(req, res) {
98 return res.json(await this.service.update(req, res));
99 }
100
101 async create(req, res) {
102 return res.json(await this.service.create(req, res));
103 }
104
105 async get(req, res) {
106 return res.json(await this.service.read(req, res));
107 }
108
109 async getAll(req, res) {
110 return res.json(await this.service.getAll(req, res));
111 }
112
113 async payFee(req, res) {
114 return res.json(await this.service.payFee(req, res));
115 }
116
117 async delete(req, res) {
118 return res.json(await this.service.read(req, res));
119 }
120}
121
122module.exports = (creditService) => {
123 const controller = new CreditController(creditService);
124
125 return controller.router;
126};
127//Credit service
128const CrudService = require("../crud/crud.service");
129const oneMonthInMs = 1000 * 60 * 60 * 24 * 31;
130class CreditService extends CrudService {
131 constructor(creditRepository, accountRepository, config, errors) {
132 super(creditRepository, errors);
133 this.creditRepository = creditRepository;
134 this.accountRepository = accountRepository;
135 this.config = config;
136 this.errors = errors;
137 }
138
139 async create(req, res) {
140 const account = await this.accountRepository.findByPk(req.body.accountId);
141 if (account.dataValues.amount < req.body.amount) {
142 return this.errors.depositNotEnoughMoney;
143 }
144
145 if (!account) {
146 return this.errors.wrongAccountId;
147 }
148
149 const updatedAccount = {
150 ...account.dataValues,
151 amount: account.dataValues.amount + req.body.amount,
152 };
153
154 await this.accountRepository.update(updatedAccount, {
155 where: { id: req.body.accountId },
156 limit: 1,
157 });
158 const credit = await this.creditRepository.create({
159 ...req.body,
160 amountReceived: 0,
161 amountWithPercent:
162 req.body.amount + req.body.amount * (req.body.percent / 100),
163 amountRemained:
164 req.body.amount + req.body.amount * (req.body.percent / 100),
165 timeEnd: new Date(Date.now() + oneMonthInMs * req.body.numberOfMonth),
166 });
167 return credit.get({ plain: true });
168 }
169
170 async payFee(req, res) {
171 const account = await this.accountRepository.findByPk(req.body.accountId);
172 const credit = await this.creditRepository.findByPk(req.body.id);
173 if (!account) {
174 return this.errors.wrongAccountId;
175 } else if (!credit) {
176 return this.errors.wrongCreditId;
177 }
178 const updatedAccount = {
179 ...account.dataValues,
180 amount: account.dataValues.amount - req.body.amount,
181 };
182
183 await this.accountRepository.update(updatedAccount, {
184 where: { id: req.body.accountId },
185 limit: 1,
186 });
187
188 const updatedCredit = {
189 ...credit.dataValues,
190 amountReceived: credit.dataValues.amountReceived
191 ? credit.dataValues.amountReceived + req.body.amount
192 : req.body.amount,
193 amountRemained: credit.dataValues.amountRemained - req.body.amount,
194 };
195
196 await this.creditRepository.update(updatedCredit, {
197 where: { id: req.body.id },
198 limit: 1,
199 });
200
201 return updatedCredit;
202 }
203
204 async getAll(req, res) {
205 const items = await this.accountRepository.findByPk(req.query.accountId, {
206 include: [
207 {
208 model: this.creditRepository,
209 },
210 ],
211 });
212 return items.Credits || [];
213 }
214}
215
216module.exports = CreditService;
217
218//Main server file
219const express = require("express");
220const bodyParser = require("body-parser");
221const jwt = require("jsonwebtoken");
222const cors = require("cors");
223const cookieParser = require("cookie-parser");
224
225const errors = require("./helpers/errors");
226const wrap = require("./helpers/wrap");
227const hash = require("./helpers/hash");
228
229const UserService = require("./modules/user/user.service");
230const AuthenticationService = require("./modules/authentication/authentication.service");
231const AccountService = require("./modules/account/account.service");
232const DepositService = require("./modules/deposit/deposit.service");
233const CreditService = require("./modules/credit/credit.service");
234
235module.exports = (db, config) => {
236 const app = express();
237
238 const userService = new UserService(db.User, config, errors);
239 const authenticationService = new AuthenticationService(
240 db.User,
241 db.Admin,
242 jwt,
243 hash,
244 config,
245 errors
246 );
247 const accountService = new AccountService(
248 db.Account,
249 db.User,
250 config,
251 errors
252 );
253
254 const depositService = new DepositService(
255 db.Deposit,
256 db.Account,
257 config,
258 errors
259 );
260
261 const creditService = new CreditService(
262 db.Credit,
263 db.Account,
264 config,
265 errors
266 );
267
268 const logger = require("./global-controllers/logger");
269 const authenticator = require("./global-controllers/authentication")(
270 jwt,
271 wrap,
272 config,
273 errors
274 );
275 const authorization = require("./global-controllers/authorization")(
276 wrap,
277 config,
278 errors
279 );
280 const error = require("./global-controllers/error");
281
282 const apiController = require("./api")(
283 userService,
284 accountService,
285 depositService,
286 creditService,
287 authenticationService,
288 config,
289 express
290 );
291
292 const getAllDeposits = async () => {
293 const deposits = await db.Deposit.findAll();
294 deposits.map(async (deposit) => {
295 const createdTime = deposit.dataValues.createdAt.getTime();
296 const dateNow = Date.now();
297 const differenceTime = Math.floor((dateNow - createdTime) / 1000);
298 await db.Deposit.update(
299 {
300 ...deposit.dataValues,
301 earnedAmount: (
302 deposit.dataValues.amount *
303 (deposit.dataValues.percent / 100) *
304 differenceTime
305 ).toFixed(2),
306 },
307 {
308 where: { id: deposit.dataValues.id },
309 limit: 1,
310 }
311 );
312 });
313 };
314
315 setInterval(getAllDeposits, 1000 * 10);
316
317 // Mounting
318 app.use(cors({ origin: true, credentials: true }));
319 app.use(bodyParser.json({ limit: "50mb" }));
320 app.use(cookieParser("secret key"));
321 app.use("/api", logger);
322 app.use("/api", authenticator);
323 app.use("/api", authorization);
324 app.use("/api", apiController);
325 app.use("/api", error);
326 return app;
327};
328
329// Wallet page
330import React, { useEffect } from "react";
331import { useUser } from "../../context/UserProvider";
332import UserCard from "../../components/UserCard";
333import AccountCard from "../../components/AcccountCard";
334import api from "../../services";
335import styles from "./styles.module.scss";
336import { useAccount } from "../../context/AccountProvider";
337import HeaderContainer from "../../components/HeaderContainer";
338
339const Wallet = () => {
340 const user = useUser();
341 const account = useAccount();
342
343 useEffect(() => {
344 (async () => {
345 const data = await api.user.getUser();
346 user.setUser(data);
347 })();
348
349 (async () => {
350 const accounts = await api.account.get();
351 account.setAccount(accounts);
352 })();
353 }, []);
354
355 return (
356 <HeaderContainer>
357 <div className={styles.root}>
358 <AccountCard />
359 </div>
360 </HeaderContainer>
361 );
362};
363
364export default Wallet;
365
366// Account card
367import React, { useState } from "react";
368import { useHistory } from "react-router";
369import ItemContainer from "../ItemContainer";
370import CardContainer from "../CardContainer";
371import { TextField, Typography } from "@material-ui/core";
372import api from "../../services";
373import { useAccount } from "../../context/AccountProvider";
374import styles from "./styles.module.scss";
375
376const AccountCard = () => {
377 const [amount, setAmount] = useState({ value: "", error: "" });
378 const account = useAccount();
379 const history = useHistory();
380 const handleSave = async () => {
381 const newAccount = await api.account.create({ amount: amount.value });
382 if (newAccount.address) {
383 const accounts = await api.account.get();
384 account.setAccount(accounts);
385 }
386 };
387
388 const Item = ({ item }) => {
389 return (
390 <CardContainer
391 className={styles.item}
392 onClick={() => {
393 history.push({
394 pathname: `/wallet/${item.address}`,
395 state: { account: item },
396 });
397 }}
398 >
399 <Typography variant="h5">Address: {item.address}</Typography>
400 <Typography variant="h6">Amount: ${item.amount}</Typography>
401 </CardContainer>
402 );
403 };
404
405 return (
406 <ItemContainer
407 title="Accounts"
408 onAdd={handleSave}
409 Item={Item}
410 items={account.account}
411 className={styles.card}
412 addTitle="Create account"
413 addDescription="Please enter the amount of new account. It like pass the amount into input field and new account would be created with the amount that you passed in."
414 >
415 <TextField
416 label="Amount"
417 variant="standard"
418 className={styles.input}
419 onChange={(e) => setAmount({ value: e.target.value })}
420 />
421 </ItemContainer>
422 );
423};
424
425export default AccountCard;