sequelize-game-bank 5.0.2 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cache_ggshield +1 -0
- package/config/setup-env.js +43 -24
- package/database/models/Credit.js +35 -22
- package/database/services/account/create-credit.js +70 -27
- package/database/services/bot/cron-schedule.js +155 -41
- package/package.json +1 -1
- package/public/credit.html +139 -81
- package/public/home.html +427 -344
- package/public/js/js.js +191 -88
- package/public/uploads/avatars/Pavel_1793516096895.png +0 -0
- package/public/uploads/avatars/root_1793520405521.png +0 -0
- package/server/server.js +304 -88
package/server/server.js
CHANGED
|
@@ -1,234 +1,450 @@
|
|
|
1
|
-
const path = require(
|
|
2
|
-
const jwt = require(
|
|
3
|
-
const bcrypt = require(
|
|
4
|
-
const User = require(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const jwt = require("jsonwebtoken");
|
|
3
|
+
const bcrypt = require("bcrypt");
|
|
4
|
+
const User = require(
|
|
5
|
+
path.join(__dirname, "..", "database", "models", "User.js"),
|
|
6
|
+
);
|
|
7
|
+
const Account = require(
|
|
8
|
+
path.join(__dirname, "..", "database", "models", "Account.js"),
|
|
9
|
+
);
|
|
10
|
+
const Credit = require(
|
|
11
|
+
path.join(__dirname, "..", "database", "models", "Credit.js"),
|
|
12
|
+
);
|
|
13
|
+
const sequelize = require(
|
|
14
|
+
path.join(__dirname, "..", "database", "config", "init-db.js"),
|
|
15
|
+
);
|
|
16
|
+
const multer = require("multer");
|
|
17
|
+
const fs = require("fs");
|
|
8
18
|
const express = require("express");
|
|
9
19
|
const logger = require("custom-color-logs").print;
|
|
10
20
|
const storage = multer.diskStorage({
|
|
11
21
|
destination: function (req, file, cb) {
|
|
12
|
-
const dir = path.join(__dirname,
|
|
22
|
+
const dir = path.join(__dirname, "..", "public", "uploads", "avatars");
|
|
13
23
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
14
24
|
cb(null, dir);
|
|
15
25
|
},
|
|
16
26
|
filename: function (req, file, cb) {
|
|
17
27
|
const ext = path.extname(file.originalname);
|
|
18
28
|
cb(null, `${req.user.name}_${Date.now()}${ext}`);
|
|
19
|
-
}
|
|
29
|
+
},
|
|
20
30
|
});
|
|
21
31
|
const upload = multer({
|
|
22
32
|
storage: storage,
|
|
23
33
|
fileFilter: function (req, file, cb) {
|
|
24
34
|
const filetypes = /jpeg|jpg|png|gif/;
|
|
25
|
-
const extname = filetypes.test(
|
|
35
|
+
const extname = filetypes.test(
|
|
36
|
+
path.extname(file.originalname).toLowerCase(),
|
|
37
|
+
);
|
|
26
38
|
if (extname) return cb(null, true);
|
|
27
|
-
cb(new Error(
|
|
39
|
+
cb(new Error("Разрешены только изображения (png, jpg, jpeg, gif)!"));
|
|
28
40
|
},
|
|
29
|
-
limits: { fileSize: 2 * 1024 * 1024 }
|
|
41
|
+
limits: { fileSize: 2 * 1024 * 1024 },
|
|
30
42
|
});
|
|
31
|
-
const usersHub = require(
|
|
32
|
-
|
|
43
|
+
const usersHub = require(
|
|
44
|
+
path.join(__dirname, "..", "database", "services", "users.js"),
|
|
45
|
+
);
|
|
46
|
+
const accountsHub = require(
|
|
47
|
+
path.join(__dirname, "..", "database", "services", "accounts.js"),
|
|
48
|
+
);
|
|
33
49
|
|
|
34
50
|
function verifySession(req, res, next) {
|
|
35
51
|
const token = req.cookies.token;
|
|
36
52
|
if (!token) {
|
|
37
|
-
return res.status(401).json({
|
|
53
|
+
return res.status(401).json({
|
|
54
|
+
status: false,
|
|
55
|
+
statusCode: -2,
|
|
56
|
+
message: "Доступ запрещен. Вы не авторизованы.",
|
|
57
|
+
});
|
|
38
58
|
}
|
|
39
59
|
try {
|
|
40
60
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
41
61
|
req.user = decoded;
|
|
42
62
|
next();
|
|
43
63
|
} catch (err) {
|
|
44
|
-
return res.status(403).json({
|
|
64
|
+
return res.status(403).json({
|
|
65
|
+
status: false,
|
|
66
|
+
statusCode: -2,
|
|
67
|
+
message: "Сессия устарела. Войдите снова.",
|
|
68
|
+
});
|
|
45
69
|
}
|
|
46
70
|
}
|
|
47
71
|
module.exports = function registerRoutes() {
|
|
48
72
|
const get = express.Router();
|
|
49
73
|
const post = express.Router();
|
|
50
|
-
post.post(
|
|
74
|
+
post.post("/users/generate-temporary-2fa", async (req, res) => {
|
|
51
75
|
try {
|
|
52
|
-
const otplib = require(
|
|
76
|
+
const otplib = require("otplib");
|
|
53
77
|
const { name } = req.body;
|
|
54
78
|
const temporarySecret = otplib.authenticator.generateSecret();
|
|
55
|
-
const otpauthUrl = otplib.authenticator.keyuri(
|
|
56
|
-
|
|
79
|
+
const otpauthUrl = otplib.authenticator.keyuri(
|
|
80
|
+
name || "User",
|
|
81
|
+
"City-Bank",
|
|
82
|
+
temporarySecret,
|
|
83
|
+
);
|
|
84
|
+
return res.json({
|
|
85
|
+
status: true,
|
|
86
|
+
secret: temporarySecret,
|
|
87
|
+
otpauthUrl: otpauthUrl,
|
|
88
|
+
});
|
|
57
89
|
} catch (err) {
|
|
58
90
|
return res.status(500).json({ status: false, message: err.message });
|
|
59
91
|
}
|
|
60
|
-
})
|
|
61
|
-
post.post(
|
|
92
|
+
});
|
|
93
|
+
post.post("/users/verify-temporary-2fa", async (req, res) => {
|
|
62
94
|
try {
|
|
63
|
-
const otplib = require(
|
|
95
|
+
const otplib = require("otplib");
|
|
64
96
|
const { secret, code } = req.body;
|
|
65
97
|
if (!secret || !code) {
|
|
66
|
-
return res.json({
|
|
98
|
+
return res.json({
|
|
99
|
+
status: false,
|
|
100
|
+
message: "Введите 6-значный код подтверждения",
|
|
101
|
+
});
|
|
67
102
|
}
|
|
68
103
|
const isValid = otplib.authenticator.check(code, secret);
|
|
69
104
|
if (isValid) {
|
|
70
|
-
return res.json({ status: true, message:
|
|
105
|
+
return res.json({ status: true, message: "Код успешно подтвержден" });
|
|
71
106
|
} else {
|
|
72
|
-
return res.json({
|
|
107
|
+
return res.json({
|
|
108
|
+
status: false,
|
|
109
|
+
message:
|
|
110
|
+
"Неверный одноразовый код токена! Проверьте время на устройстве.",
|
|
111
|
+
});
|
|
73
112
|
}
|
|
74
113
|
} catch (err) {
|
|
75
114
|
return res.status(500).json({ status: false, message: err.message });
|
|
76
115
|
}
|
|
77
116
|
});
|
|
78
|
-
post.post(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
117
|
+
post.post(
|
|
118
|
+
"/users/avatar",
|
|
119
|
+
verifySession,
|
|
120
|
+
upload.single("avatar"),
|
|
121
|
+
async (req, res) => {
|
|
122
|
+
if (!req.file) {
|
|
123
|
+
return res
|
|
124
|
+
.status(400)
|
|
125
|
+
.json({ status: false, statusCode: -2, message: "Файл не загружен" });
|
|
126
|
+
}
|
|
127
|
+
const avatarUrl = `/api/bank/uploads/avatars/${req.file.filename}`;
|
|
128
|
+
const result = await usersHub.changeAvatar(req.user.name, avatarUrl);
|
|
129
|
+
return res.json(result);
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
post.get("/users/profile", verifySession, async (req, res) => {
|
|
133
|
+
const user = await User.findByPk(req.user.name, {
|
|
134
|
+
attributes: ["name", "user_gmail", "avatar"],
|
|
135
|
+
});
|
|
88
136
|
if (!user) return res.status(404).json({ status: false });
|
|
89
137
|
return res.json({ status: true, user });
|
|
90
138
|
});
|
|
91
|
-
post.get(
|
|
139
|
+
post.get("/users/check-nik", async (req, res) => {
|
|
92
140
|
const user = await User.findByPk(req.query.name);
|
|
93
141
|
if (user) {
|
|
94
|
-
return res.json({
|
|
142
|
+
return res.json({
|
|
143
|
+
status: false,
|
|
144
|
+
statusCode: -20,
|
|
145
|
+
message: "Никнейм уже занят!",
|
|
146
|
+
});
|
|
95
147
|
}
|
|
96
148
|
return res.json({ status: true, statusCode: 1 });
|
|
97
149
|
});
|
|
98
|
-
post.get(
|
|
150
|
+
post.get("/users/check-gmail", async (req, res) => {
|
|
99
151
|
const user = await User.findOne({ where: { user_gmail: req.query.gmail } });
|
|
100
152
|
if (user) {
|
|
101
|
-
return res.json({
|
|
153
|
+
return res.json({
|
|
154
|
+
status: false,
|
|
155
|
+
statusCode: -21,
|
|
156
|
+
message: "Данный email уже используется!",
|
|
157
|
+
});
|
|
102
158
|
}
|
|
103
159
|
return res.json({ status: true, statusCode: 1 });
|
|
104
160
|
});
|
|
105
|
-
post.post(
|
|
161
|
+
post.post("/users/register", async (req, res) => {
|
|
106
162
|
const result = await usersHub.registerUser(req.body);
|
|
107
163
|
if (!result.status) return res.status(400).json(result);
|
|
108
164
|
return res.status(201).json(result);
|
|
109
165
|
});
|
|
110
|
-
post.post(
|
|
166
|
+
post.post("/users/login", async (req, res) => {
|
|
111
167
|
const { name, password, totpCode } = req.body;
|
|
112
168
|
const result = await usersHub.login(name, password, totpCode);
|
|
113
169
|
if (!result.status) {
|
|
114
170
|
if (result.require2FA) {
|
|
115
|
-
return res.status(200).json(result);
|
|
171
|
+
return res.status(200).json(result);
|
|
116
172
|
}
|
|
117
173
|
return res.status(401).json(result);
|
|
118
174
|
}
|
|
119
|
-
const sessionToken = jwt.sign({ name: name }, process.env.JWT_SECRET, {
|
|
120
|
-
|
|
175
|
+
const sessionToken = jwt.sign({ name: name }, process.env.JWT_SECRET, {
|
|
176
|
+
expiresIn: "2h",
|
|
177
|
+
});
|
|
178
|
+
res.cookie("token", sessionToken, {
|
|
179
|
+
httpOnly: true,
|
|
180
|
+
maxAge: 2 * 60 * 60 * 1000,
|
|
181
|
+
});
|
|
121
182
|
return res.json(result);
|
|
122
183
|
});
|
|
123
|
-
post.post(
|
|
124
|
-
res.clearCookie(
|
|
184
|
+
post.post("/users/logout", (req, res) => {
|
|
185
|
+
res.clearCookie("token");
|
|
125
186
|
return res.json({ status: true, statusCode: 1 });
|
|
126
187
|
});
|
|
127
|
-
post.get(
|
|
188
|
+
post.get("/users/my-total-manny", verifySession, async (req, res) => {
|
|
128
189
|
const result = await usersHub.mathManny(req.user.name);
|
|
129
|
-
return res.json({
|
|
190
|
+
return res.json({
|
|
191
|
+
status: result.status,
|
|
192
|
+
statusCode: result.statusCode,
|
|
193
|
+
totalManny: result.totalManny,
|
|
194
|
+
username: req.user.name,
|
|
195
|
+
});
|
|
130
196
|
});
|
|
131
|
-
post.post(
|
|
197
|
+
post.post("/accounts", verifySession, async (req, res) => {
|
|
132
198
|
const { pinCode, enableAccount2FA } = req.body;
|
|
133
|
-
const result = await accountsHub.create(
|
|
199
|
+
const result = await accountsHub.create(
|
|
200
|
+
req.user.name,
|
|
201
|
+
pinCode,
|
|
202
|
+
enableAccount2FA,
|
|
203
|
+
);
|
|
134
204
|
return res.json(result);
|
|
135
205
|
});
|
|
136
|
-
post.get(
|
|
206
|
+
post.get("/accounts/my", verifySession, async (req, res) => {
|
|
137
207
|
try {
|
|
138
|
-
const myAccounts = await Account.findAll({
|
|
208
|
+
const myAccounts = await Account.findAll({
|
|
209
|
+
where: { user: req.user.name },
|
|
210
|
+
});
|
|
139
211
|
return res.json({ status: true, statusCode: 1, accounts: myAccounts });
|
|
140
212
|
} catch (e) {
|
|
141
213
|
return res.status(500).json({ status: false, statusCode: -10 });
|
|
142
214
|
}
|
|
143
215
|
});
|
|
144
|
-
post.delete(
|
|
216
|
+
post.delete("/accounts/:id", verifySession, async (req, res) => {
|
|
145
217
|
try {
|
|
146
218
|
const result = await accountsHub.delete(req.params.id, req.user.name);
|
|
147
219
|
if (!result.status) {
|
|
148
220
|
return res.status(400).json({
|
|
149
221
|
status: false,
|
|
150
222
|
statusCode: result.statusCode || -11,
|
|
151
|
-
message: result.message || "Не удалось удалить счет"
|
|
223
|
+
message: result.message || "Не удалось удалить счет",
|
|
152
224
|
});
|
|
153
225
|
}
|
|
154
|
-
return res.json({
|
|
226
|
+
return res.json({
|
|
227
|
+
status: true,
|
|
228
|
+
statusCode: 1,
|
|
229
|
+
message: "Счет успешно удален",
|
|
230
|
+
});
|
|
155
231
|
} catch (err) {
|
|
156
|
-
console.log(
|
|
157
|
-
|
|
232
|
+
console.log(
|
|
233
|
+
logger.ServerError("Ошибка роута удаления счета:", err.message),
|
|
234
|
+
);
|
|
235
|
+
return res.status(500).json({
|
|
236
|
+
status: false,
|
|
237
|
+
statusCode: -10,
|
|
238
|
+
message: "Внутренняя ошибка сервера",
|
|
239
|
+
});
|
|
158
240
|
}
|
|
159
241
|
});
|
|
160
|
-
post.post(
|
|
242
|
+
post.post("/accounts/transfer", verifySession, async (req, res) => {
|
|
161
243
|
const { fromId, toId, amount, pinCode, totpCode } = req.body;
|
|
162
|
-
const result = await accountsHub.transfer(
|
|
244
|
+
const result = await accountsHub.transfer(
|
|
245
|
+
fromId,
|
|
246
|
+
toId,
|
|
247
|
+
amount,
|
|
248
|
+
pinCode,
|
|
249
|
+
totpCode,
|
|
250
|
+
);
|
|
163
251
|
return res.json(result);
|
|
164
252
|
});
|
|
165
|
-
post.post(
|
|
166
|
-
const { targetAccountId, amount, paymentsCount, autoWithdrawal, pinCode } =
|
|
167
|
-
|
|
253
|
+
post.post("/accounts/credit", verifySession, async (req, res) => {
|
|
254
|
+
const { targetAccountId, amount, paymentsCount, autoWithdrawal, pinCode } =
|
|
255
|
+
req.body;
|
|
256
|
+
const result = await accountsHub.creatCredit(
|
|
257
|
+
req.user.name,
|
|
258
|
+
targetAccountId,
|
|
259
|
+
amount,
|
|
260
|
+
pinCode,
|
|
261
|
+
paymentsCount,
|
|
262
|
+
autoWithdrawal,
|
|
263
|
+
);
|
|
168
264
|
return res.json(result);
|
|
169
265
|
});
|
|
170
|
-
post.post(
|
|
171
|
-
if (req.user.name !==
|
|
172
|
-
|
|
266
|
+
post.post("/admin/accounts/:id/status", verifySession, async (req, res) => {
|
|
267
|
+
if (req.user.name !== "root")
|
|
268
|
+
return res
|
|
269
|
+
.status(403)
|
|
270
|
+
.json({ status: false, statusCode: -2, message: "Отказано в доступе" });
|
|
271
|
+
const result = await accountsHub.changeAccountStatus(
|
|
272
|
+
req.params.id,
|
|
273
|
+
req.body.active,
|
|
274
|
+
);
|
|
173
275
|
return res.json(result);
|
|
174
276
|
});
|
|
175
|
-
post.post(
|
|
176
|
-
if (req.user.name !==
|
|
277
|
+
post.post("/admin/accounts/:id/modify", verifySession, async (req, res) => {
|
|
278
|
+
if (req.user.name !== "root")
|
|
279
|
+
return res
|
|
280
|
+
.status(403)
|
|
281
|
+
.json({ status: false, statusCode: -2, message: "Отказано в доступе" });
|
|
177
282
|
const { amount, type } = req.body;
|
|
178
283
|
let result;
|
|
179
|
-
if (type ===
|
|
284
|
+
if (type === "add") {
|
|
180
285
|
result = await accountsHub.addManny(req.params.id, amount);
|
|
181
286
|
} else {
|
|
182
287
|
result = await accountsHub.removeManny(req.params.id, amount);
|
|
183
288
|
}
|
|
184
289
|
return res.json(result);
|
|
185
290
|
});
|
|
186
|
-
post.get(
|
|
187
|
-
if (req.user.name !==
|
|
291
|
+
post.get("/admin/accounts/all", verifySession, async (req, res) => {
|
|
292
|
+
if (req.user.name !== "root")
|
|
293
|
+
return res
|
|
294
|
+
.status(403)
|
|
295
|
+
.json({ status: false, statusCode: -2, message: "Отказано в доступе" });
|
|
188
296
|
try {
|
|
189
|
-
const allAccounts = await Account.findAll({ order: [[
|
|
297
|
+
const allAccounts = await Account.findAll({ order: [["id", "ASC"]] });
|
|
190
298
|
return res.json({ status: true, statusCode: 1, accounts: allAccounts });
|
|
191
299
|
} catch (e) {
|
|
192
300
|
return res.status(500).json({ status: false, statusCode: -10 });
|
|
193
301
|
}
|
|
194
302
|
});
|
|
195
|
-
post.get(
|
|
303
|
+
post.get("/users/profile/settings", verifySession, async (req, res) => {
|
|
196
304
|
try {
|
|
197
|
-
const user = await User.findByPk(req.user.name, {
|
|
198
|
-
|
|
305
|
+
const user = await User.findByPk(req.user.name, {
|
|
306
|
+
attributes: ["name", "user_gmail", "two_fa_status", "avatar"],
|
|
307
|
+
});
|
|
308
|
+
if (!user)
|
|
309
|
+
return res
|
|
310
|
+
.status(404)
|
|
311
|
+
.json({ status: false, message: "Юзер не найден" });
|
|
199
312
|
return res.json({ status: true, user });
|
|
200
|
-
} catch(e) {
|
|
201
|
-
return res
|
|
313
|
+
} catch (e) {
|
|
314
|
+
return res
|
|
315
|
+
.status(500)
|
|
316
|
+
.json({ status: false, message: "Внутренняя ошибка бэка" });
|
|
202
317
|
}
|
|
203
318
|
});
|
|
204
|
-
post.post(
|
|
319
|
+
post.post("/users/enable-2fa", verifySession, async (req, res) => {
|
|
205
320
|
const result = await usersHub.enable2FA(req.user.name);
|
|
206
321
|
return res.json(result);
|
|
207
322
|
});
|
|
208
|
-
post.post(
|
|
323
|
+
post.post("/users/disable-2fa", verifySession, async (req, res) => {
|
|
209
324
|
const result = await usersHub.disable2FA(req.user.name);
|
|
210
325
|
return res.json(result);
|
|
211
326
|
});
|
|
212
|
-
post.get(
|
|
327
|
+
post.get("/accounts/my-logs", verifySession, async (req, res) => {
|
|
213
328
|
const result = await accountsHub.getLogs(req.user.name);
|
|
214
329
|
return res.json(result);
|
|
215
330
|
});
|
|
216
|
-
post.post(
|
|
217
|
-
if(!req.body) return res.sendStatus(400)
|
|
331
|
+
post.post("/terminal/register", async (req, res) => {
|
|
332
|
+
if (!req.body) return res.sendStatus(400);
|
|
218
333
|
const id = req.body.id;
|
|
219
334
|
const pincode = req.body.pincode;
|
|
220
335
|
const card = req.body.card;
|
|
221
|
-
const result = await accountsHub.linkACard(card, id, pincode)
|
|
222
|
-
if(!result.status) return res.sendStatus(422)
|
|
336
|
+
const result = await accountsHub.linkACard(card, id, pincode);
|
|
337
|
+
if (!result.status) return res.sendStatus(422);
|
|
223
338
|
return res.sendStatus(200);
|
|
224
339
|
});
|
|
225
|
-
post.post(
|
|
340
|
+
post.post("/terminal/transfer", async (req, res) => {
|
|
226
341
|
const card = req.body.card;
|
|
227
342
|
const to = req.body.to;
|
|
228
343
|
const pincode = req.body.pincode;
|
|
229
344
|
const manny = req.body.manny;
|
|
230
|
-
const result = await accountsHub.cardTransfer(card, to, manny, pincode)
|
|
345
|
+
const result = await accountsHub.cardTransfer(card, to, manny, pincode);
|
|
346
|
+
});
|
|
347
|
+
post.post("/accounts/credit/pay", verifySession, async (req, res) => {
|
|
348
|
+
const { creditId, amount, pinCode } = req.body;
|
|
349
|
+
const numericAmount = parseFloat(amount);
|
|
350
|
+
|
|
351
|
+
if (!creditId || isNaN(numericAmount) || numericAmount <= 0) {
|
|
352
|
+
return res
|
|
353
|
+
.status(400)
|
|
354
|
+
.json({ status: false, message: "Некорректная сумма или ID кредита" });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const transaction = await sequelize.transaction();
|
|
358
|
+
try {
|
|
359
|
+
const credit = await Credit.findByPk(creditId, {
|
|
360
|
+
transaction,
|
|
361
|
+
lock: transaction.LOCK.UPDATE,
|
|
362
|
+
});
|
|
363
|
+
if (!credit || credit.status !== "active")
|
|
364
|
+
throw new Error("Активный кредит не найден");
|
|
365
|
+
|
|
366
|
+
const targetAccountId = credit.accountId || credit.account_id;
|
|
367
|
+
const account = await Account.findByPk(targetAccountId, {
|
|
368
|
+
transaction,
|
|
369
|
+
lock: transaction.LOCK.UPDATE,
|
|
370
|
+
});
|
|
371
|
+
if (!account) throw new Error("Счет не найден");
|
|
372
|
+
if (account.user !== req.user.name)
|
|
373
|
+
throw new Error("Нет доступа к этому счету");
|
|
374
|
+
if (account.status === "blocked") throw new Error("Счет заблокирован");
|
|
375
|
+
|
|
376
|
+
const bcrypt = require("bcrypt");
|
|
377
|
+
const isPinValid = await bcrypt.compare(
|
|
378
|
+
pinCode,
|
|
379
|
+
account.pincode || account.pin_code,
|
|
380
|
+
);
|
|
381
|
+
if (!isPinValid) throw new Error("Неверный PIN-код счета");
|
|
382
|
+
|
|
383
|
+
const balanceField = account.manny !== undefined ? "manny" : "balance";
|
|
384
|
+
const currentBalance = parseFloat(account[balanceField]);
|
|
385
|
+
|
|
386
|
+
if (currentBalance < numericAmount)
|
|
387
|
+
throw new Error("Недостаточно средств на счете");
|
|
388
|
+
|
|
389
|
+
const finalPayment = Math.min(
|
|
390
|
+
numericAmount,
|
|
391
|
+
parseFloat(credit.remainingRepay),
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
await account.decrement(balanceField, { by: finalPayment, transaction });
|
|
395
|
+
|
|
396
|
+
credit.remainingRepay = parseFloat(credit.remainingRepay) - finalPayment;
|
|
397
|
+
if (credit.remainingRepay <= 0) {
|
|
398
|
+
credit.status = "paid";
|
|
399
|
+
}
|
|
400
|
+
await credit.save({ transaction });
|
|
401
|
+
|
|
402
|
+
if (typeof accountsHub.getLogs === "function") {
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
await transaction.commit();
|
|
406
|
+
return res.json({
|
|
407
|
+
status: true,
|
|
408
|
+
message:
|
|
409
|
+
credit.status === "paid"
|
|
410
|
+
? "Кредит полностью погашен!"
|
|
411
|
+
: `Успешно оплачено ${finalPayment.toFixed(2)} руб.`,
|
|
412
|
+
remainingRepay: credit.remainingRepay,
|
|
413
|
+
});
|
|
414
|
+
} catch (error) {
|
|
415
|
+
if (transaction && !transaction.finished) await transaction.rollback();
|
|
416
|
+
return res.status(400).json({ status: false, message: error.message });
|
|
417
|
+
}
|
|
231
418
|
});
|
|
232
|
-
|
|
419
|
+
post.get("/accounts/my-current-credit", verifySession, async (req, res) => {
|
|
420
|
+
try {
|
|
421
|
+
const myAccounts = await Account.findAll({
|
|
422
|
+
where: { user: req.user.name },
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
if (!myAccounts || myAccounts.length === 0) {
|
|
426
|
+
return res.json({ status: true, activeCredit: null });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const accountIds = myAccounts.map((acc) => acc.id);
|
|
430
|
+
|
|
431
|
+
const { Op } = require("sequelize");
|
|
432
|
+
const activeCredit = await Credit.findOne({
|
|
433
|
+
where: {
|
|
434
|
+
status: "active",
|
|
435
|
+
accountId: accountIds,
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
return res.json({ status: true, activeCredit: activeCredit || null });
|
|
440
|
+
} catch (error) {
|
|
441
|
+
console.error("Ошибка при получении текущего кредита:", error.message);
|
|
442
|
+
return res
|
|
443
|
+
.status(500)
|
|
444
|
+
.json({ status: false, message: "Внутренняя ошибка сервера" });
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return { GET: get, POST: post };
|
|
233
449
|
};
|
|
234
|
-
module.exports.verifySession = verifySession;
|
|
450
|
+
module.exports.verifySession = verifySession;
|