sequelize-game-bank 5.0.0 → 5.0.2

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/public/js/js.js CHANGED
@@ -1,620 +1,865 @@
1
- const toastifyScript = document.createElement('script');
2
- toastifyScript.src = 'https://cdn.jsdelivr.net/npm/toastify-js';
1
+ const toastifyScript = document.createElement("script");
2
+ toastifyScript.src = "https://cdn.jsdelivr.net/npm/toastify-js";
3
3
  document.head.append(toastifyScript);
4
4
  let currentUsername = "";
5
- function showTab(tabId) {
6
- document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
7
- document.querySelectorAll('.menu-btn').forEach(b => b.classList.remove('active'));
8
- document.getElementById(tabId).classList.add('active');
9
- event.target.classList.add('active');
10
- }
11
- window.tippy('#general', {
12
- content: "Главная",
13
- placement:'top-end',
14
- arrow: false,
15
- animation:'scale-extreme',
16
- theme:"light"
17
- });
18
- window.tippy('#acccount', {
19
- content: "Щета",
20
- placement:'top-end',
21
- arrow: false,
22
- animation:'scale-extreme',
23
- theme:"light"
24
- });
25
- window.tippy('#transfer', {
26
- content: "Перевод",
27
- placement:'top-end',
28
- arrow: false,
29
- animation:'scale-extreme',
30
- theme:"light"
31
- });
32
- window.tippy('#history', {
33
- content: "История",
34
- placement:'top-end',
35
- arrow: false,
36
- animation:'scale-extreme',
37
- theme:"light"
38
- });
39
- window.tippy('#credit', {
40
- content: "Кредит",
41
- placement:'top-end',
42
- arrow: false,
43
- animation:'scale-extreme',
44
- theme:"light"
45
- });
46
- window.tippy('#profile', {
47
- content: "Редактировать профиль",
48
- placement:'top-end',
49
- arrow: false,
50
- animation:'scale-extreme',
51
- theme:"light"
52
- });
53
- async function loadData() {
54
- try {
55
- let response = await fetch('/api/bank/accounts/my');
56
- if (response.status === 401 || response.status === 403) {
57
- window.Swal.fire({
58
- title: 'Внимание!',
59
- text: 'Сессия устарела!',
60
- icon: 'warning',
61
- iconColor:"orange",
62
- confirmButtonText: 'ОК',
63
- confirmButtonColor: '#3085d6',
64
- background:'#d6cb3089'
65
- });
66
- window.location.href = '/bank/login.html';
67
- return;
68
- }
69
- let result = await response.json();
70
- let mannyRes = await fetch('/api/bank/users/my-total-manny');
71
- let mannyData = await mannyRes.json();
72
- currentUsername = mannyData.username;
73
- const userDisplay = document.getElementById('user-display');
74
- if (userDisplay) userDisplay.innerText = currentUsername.toUpperCase();
75
- const userDisplayGeneral = document.getElementById('user-display-page');
76
- if (userDisplayGeneral) userDisplayGeneral.innerText = currentUsername.toUpperCase();
77
- const totalManny = document.getElementById('total-manny');
78
- if (totalManny) totalManny.innerText = mannyData.totalManny.toFixed(2);
79
- const adminNavButton = document.getElementById('admin-nav');
80
- if (adminNavButton) {
81
- if (currentUsername === 'root') {
82
- Toastify({
83
- text: "Здраствуйте админестратор!",
84
- duration: 3000,
85
- close: true,
86
- gravity: "top",
87
- position: "right",
88
- stopOnFocus: true,
89
- style: {
90
- background: "linear-gradient(to right, green, blue)",
91
- }
92
- }).showToast();
93
- adminNavButton.style.display = 'block';
94
- } else {
95
- Toastify({
96
- text: `Здраствуйте, ${currentUsername}`,
97
- duration: 3000,
98
- close: true,
99
- gravity: "top",
100
- position: "right",
101
- stopOnFocus: true,
102
- style: {
103
- background: "linear-gradient(to right, green, blue)",
104
- }
105
- }).showToast();
106
- adminNavButton.style.display = 'none';
107
- }
108
- }
109
- const tbody = document.querySelector('#table-accounts tbody');
110
- const selectFrom = document.getElementById('tx-from');
111
- const selectCredit = document.getElementById('credit-target');
112
- if (tbody) tbody.innerHTML = "";
113
- if (selectFrom) selectFrom.innerHTML = "";
114
- if (selectCredit) selectCredit.innerHTML = "";
115
- if (result.accounts && Array.isArray(result.accounts)) {
116
- result.accounts.forEach(acc => {
117
- if (tbody) {
118
- tbody.innerHTML += `
119
- <tr>
120
- <td>#${acc.id}</td>
121
- <td><b>${parseFloat(acc.manny).toFixed(2)}</b> руб.</td>
122
- <td>${acc.two_fa_status ? '🔒 Активна' : '❌ Выкл'}</td>
123
- <td>${acc.active ? '<span style="color:green">Активен</span>' : '<span style="color:red">Забанен</span>'}</td>
124
- <td><button class="btn btn-danger" onclick="closeAccount(${acc.id})">Закрыть</button></td>
125
- </tr>`;
126
- }
127
-
128
- if (acc.active) {
129
- let opt = `<option value="${acc.id}">Счет #${acc.id} (${parseFloat(acc.manny).toFixed(2)} руб.)</option>`;
130
- if (selectFrom) selectFrom.innerHTML += opt;
131
- if (selectCredit) selectCredit.innerHTML += opt;
132
- }
133
- });
134
- }
135
- let profileRes = await fetch('/api/bank/users/profile');
136
- let profileData = await profileRes.json();
137
- const userAvatar = document.getElementById('user-avatar');
138
- window.tippy("#user-avatar", {
139
- content:`<span style='color:blue'>${currentUsername.toLocaleUpperCase()}</span> — <span style='color:lightgreen;'>${mannyData.totalManny.toFixed(2)}</span>`,
140
- placement:"right",
141
- allowHTML:true,
142
- theme:'user-info',
143
- arrow:false
144
- })
145
- const userAvatar2 = document.getElementById('user-avatar2');
146
- if (profileData.status && profileData.user && profileData.user.avatar && userAvatar) {
147
- userAvatar.src = profileData.user.avatar;
148
- }
149
- if (profileData.status && profileData.user && profileData.user.avatar && userAvatar2) {
150
- userAvatar2.src = profileData.user.avatar;
151
- }
152
- await syncProfileSectionData();
153
- if (document.getElementById('table-logs')) {
154
- await loadTransactionLogs();
155
- }
156
- } catch(e) {
157
- console.error("Критическая ошибка выполнения скрипта страницы:", e);
5
+ function showTab(tabId) {
6
+ document
7
+ .querySelectorAll(".section")
8
+ .forEach((s) => s.classList.remove("active"));
9
+ document
10
+ .querySelectorAll(".menu-btn")
11
+ .forEach((b) => b.classList.remove("active"));
12
+ document.getElementById(tabId).classList.add("active");
13
+ event.target.classList.add("active");
14
+ }
15
+ window.tippy("#general", {
16
+ content: "Главная",
17
+ placement: "top-end",
18
+ arrow: false,
19
+ animation: "scale-extreme",
20
+ theme: "light",
21
+ });
22
+ window.tippy("#acccount", {
23
+ content: "Щета",
24
+ placement: "top-end",
25
+ arrow: false,
26
+ animation: "scale-extreme",
27
+ theme: "light",
28
+ });
29
+ window.tippy("#transfer", {
30
+ content: "Перевод",
31
+ placement: "top-end",
32
+ arrow: false,
33
+ animation: "scale-extreme",
34
+ theme: "light",
35
+ });
36
+ window.tippy("#history", {
37
+ content: "История",
38
+ placement: "top-end",
39
+ arrow: false,
40
+ animation: "scale-extreme",
41
+ theme: "light",
42
+ });
43
+ window.tippy("#credit", {
44
+ content: "Кредит",
45
+ placement: "top-end",
46
+ arrow: false,
47
+ animation: "scale-extreme",
48
+ theme: "light",
49
+ });
50
+ window.tippy("#profile", {
51
+ content: "Редактировать профиль",
52
+ placement: "top-end",
53
+ arrow: false,
54
+ animation: "scale-extreme",
55
+ theme: "light",
56
+ });
57
+ async function loadData() {
58
+ try {
59
+ let response = await fetch("/api/bank/accounts/my", {
60
+ headers: {
61
+ "Bypass-Tunnel-Reminder": "true",
62
+ },
63
+ });
64
+ if (response.status === 401 || response.status === 403) {
158
65
  window.Swal.fire({
159
- title: 'Внимание!',
160
- text: `Ошибка кода!${e}`,
161
- icon: 'error',
162
- confirmButtonText: 'ОК',
163
- confirmButtonColor: '#d63030',
164
- background: '#d63030'
66
+ title: "Внимание!",
67
+ text: "Сессия устарела!",
68
+ icon: "warning",
69
+ iconColor: "orange",
70
+ confirmButtonText: "ОК",
71
+ confirmButtonColor: "#3085d6",
72
+ background: "#d6cb3089",
165
73
  });
74
+ window.location.href = "/bank/login.html";
75
+ return;
166
76
  }
167
- }
168
- async function openCreateAccountModal() {
169
- const pin = await window.Swal.fire({title: 'Создание щёта', text: 'Введите надёжный пин-код!', icon: 'question', iconColor:"blue", input:"number", inputAttributes: { autocapitalize: "off" }, preConfirm:async(pincode)=>{return pincode}, confirmButtonText: 'ОК', confirmButtonColor: '#3085d6', background:'#ffffffaf'})
170
- if (!pin.value) return;
171
- const fa = await Swal.fire({title: 'Создание щёта', text: 'Включить индивидуальную 2FA-защиту транзакций для этого счета?', icon: 'question', iconColor:"blue", showCancelButton: true, confirmButtonColor: '#3085d6', cancelButtonColor: '#3085d6', confirmButtonText: 'Да, включить', cancelButtonText: 'Нет, не включать'}).then((result) => {return result;});
172
- const res = await fetch('/api/bank/accounts', {
173
- method: 'POST',
174
- headers: {'Content-Type': 'application/json'},
175
- body: JSON.stringify({ pinCode: pin.value, enableAccount2FA: fa.isConfirmed })
77
+ let result = await response.json();
78
+ let mannyRes = await fetch("/api/bank/users/my-total-manny", {
79
+ headers: {
80
+ "Bypass-Tunnel-Reminder": "true",
81
+ },
176
82
  });
177
- const data = await res.json();
178
- if(data.status) {
179
- loadData();
180
- Toastify({
181
- text: `Вы создали щёт! \n Проверте "Мои щета"`,
83
+ let mannyData = await mannyRes.json();
84
+ currentUsername = mannyData.username;
85
+ const userDisplay = document.getElementById("user-display");
86
+ if (userDisplay) userDisplay.innerText = currentUsername.toUpperCase();
87
+ const userDisplayGeneral = document.getElementById("user-display-page");
88
+ if (userDisplayGeneral)
89
+ userDisplayGeneral.innerText = currentUsername.toUpperCase();
90
+ const totalManny = document.getElementById("total-manny");
91
+ if (totalManny) totalManny.innerText = mannyData.totalManny.toFixed(2);
92
+ const adminNavButton = document.getElementById("admin-nav");
93
+ if (adminNavButton) {
94
+ if (currentUsername === "root") {
95
+ Toastify({
96
+ text: "Здраствуйте админестратор!",
182
97
  duration: 3000,
183
98
  close: true,
184
99
  gravity: "top",
185
100
  position: "right",
186
101
  stopOnFocus: true,
187
102
  style: {
188
- background: "linear-gradient(to right, yellowgreen, green)",
189
- }
103
+ background: "linear-gradient(to right, green, blue)",
104
+ },
190
105
  }).showToast();
191
- } else {
192
- Toastify({
193
- text: `cоздание щёта не удалось!`,
194
- duration:3000,
195
- close:true,
196
- style: { background: "RGB(245, 158, 11)" }
197
- }).showToast();
198
- console.error("Ошибка: " + data.message)
106
+ adminNavButton.style.display = "block";
107
+ } else {
108
+ Toastify({
109
+ text: `Здраствуйте, ${currentUsername}`,
110
+ duration: 3000,
111
+ close: true,
112
+ gravity: "top",
113
+ position: "right",
114
+ stopOnFocus: true,
115
+ style: {
116
+ background: "linear-gradient(to right, green, blue)",
117
+ },
118
+ }).showToast();
119
+ adminNavButton.style.display = "none";
120
+ }
199
121
  }
200
- }
201
- async function executeTransfer() {
202
- const fromId = document.getElementById('tx-from').value;
203
- const toId = document.getElementById('tx-to').value.trim();
204
- const amount = document.getElementById('tx-amount').value;
205
- const pinCode = document.getElementById('tx-pin').value;
206
- const fa = await Swal.fire({title: 'Перевод', text: 'Вы подтверждаете перевод?', icon: 'question', iconColor:"blue", showCancelButton: true, confirmButtonColor: '#3085d6', cancelButtonColor: '#3085d6', confirmButtonText: 'Да, перевести', cancelButtonText: 'Нет, не переводить'}).then((result) => {return result;});
207
- if(!fa.isConfirmed) return
122
+ const tbody = document.querySelector("#table-accounts tbody");
123
+ const selectFrom = document.getElementById("tx-from");
124
+ const selectCredit = document.getElementById("credit-target");
125
+ if (tbody) tbody.innerHTML = "";
126
+ if (selectFrom) selectFrom.innerHTML = "";
127
+ if (selectCredit) selectCredit.innerHTML = "";
128
+ if (result.accounts && Array.isArray(result.accounts)) {
129
+ result.accounts.forEach((acc) => {
130
+ if (tbody) {
131
+ tbody.innerHTML += `
132
+ <tr>
133
+ <td>#${acc.id}</td>
134
+ <td><b>${parseFloat(acc.manny).toFixed(2)}</b> руб.</td>
135
+ <td>${acc.two_fa_status ? "🔒 Активна" : "❌ Выкл"}</td>
136
+ <td>${acc.active ? '<span style="color:green">Активен</span>' : '<span style="color:red">Забанен</span>'}</td>
137
+ <td><button class="btn btn-danger" onclick="closeAccount(${acc.id})">Закрыть</button></td>
138
+ </tr>`;
139
+ }
208
140
 
209
- const res = await fetch('/api/bank/accounts/transfer', {
210
- method: 'POST',
211
- headers: {'Content-Type': 'application/json'},
212
- body: JSON.stringify({ fromId, toId, amount, pinCode })
141
+ if (acc.active) {
142
+ let opt = `<option value="${acc.id}">Счет #${acc.id} (${parseFloat(acc.manny).toFixed(2)} руб.)</option>`;
143
+ if (selectFrom) selectFrom.innerHTML += opt;
144
+ if (selectCredit) selectCredit.innerHTML += opt;
145
+ }
146
+ });
147
+ }
148
+ let profileRes = await fetch("/api/bank/users/profile", {
149
+ headers: {
150
+ "Bypass-Tunnel-Reminder": "true",
151
+ },
213
152
  });
214
- const data = await res.json();
215
- if (data.status) {
216
- Toastify({
217
- text: `Вы перевели ${amount} на другой щёт\n #${fromId} → #${toId}`,
218
- duration:3000,
219
- style: { background: "rgb(58, 245, 11)" }
220
- }).showToast();
221
- loadData();
222
- } else {
223
- Toastify({
224
- text: `Перевод средств не удался\n #${fromId} → #${toId}`,
225
- duration:3000,
226
- style: { background: "RGB(245, 158, 11)" }
227
- }).showToast();
228
- console.error("Ошибка ["+data.statusCode+"]: " + data.message);
153
+ let profileData = await profileRes.json();
154
+ const userAvatar = document.getElementById("user-avatar");
155
+ window.tippy("#user-avatar", {
156
+ content: `<span style='color:blue'>${currentUsername.toLocaleUpperCase()}</span> — <span style='color:lightgreen;'>${mannyData.totalManny.toFixed(2)}</span>`,
157
+ placement: "right",
158
+ allowHTML: true,
159
+ theme: "user-info",
160
+ arrow: false,
161
+ });
162
+ const userAvatar2 = document.getElementById("user-avatar2");
163
+ if (
164
+ profileData.status &&
165
+ profileData.user &&
166
+ profileData.user.avatar &&
167
+ userAvatar
168
+ ) {
169
+ userAvatar.src = profileData.user.avatar;
170
+ }
171
+ if (
172
+ profileData.status &&
173
+ profileData.user &&
174
+ profileData.user.avatar &&
175
+ userAvatar2
176
+ ) {
177
+ userAvatar2.src = profileData.user.avatar;
229
178
  }
179
+ await syncProfileSectionData();
180
+ if (document.getElementById("table-logs")) {
181
+ await loadTransactionLogs();
182
+ }
183
+ } catch (e) {
184
+ console.error("Критическая ошибка выполнения скрипта страницы:", e);
185
+ window.Swal.fire({
186
+ title: "Внимание!",
187
+ text: `Ошибка кода!${e}`,
188
+ icon: "error",
189
+ confirmButtonText: "ОК",
190
+ confirmButtonColor: "#d63030",
191
+ background: "#d63030",
192
+ });
230
193
  }
194
+ }
195
+ async function openCreateAccountModal() {
196
+ const pin = await window.Swal.fire({
197
+ title: "Создание щёта",
198
+ text: "Введите надёжный пин-код!",
199
+ icon: "question",
200
+ iconColor: "blue",
201
+ input: "number",
202
+ inputAttributes: { autocapitalize: "off" },
203
+ preConfirm: async (pincode) => {
204
+ return pincode;
205
+ },
206
+ confirmButtonText: "ОК",
207
+ confirmButtonColor: "#3085d6",
208
+ background: "#ffffffaf",
209
+ });
210
+ if (!pin.value) return;
211
+ const fa = await Swal.fire({
212
+ title: "Создание щёта",
213
+ text: "Включить индивидуальную 2FA-защиту транзакций для этого счета?",
214
+ icon: "question",
215
+ iconColor: "blue",
216
+ showCancelButton: true,
217
+ confirmButtonColor: "#3085d6",
218
+ cancelButtonColor: "#3085d6",
219
+ confirmButtonText: "Да, включить",
220
+ cancelButtonText: "Нет, не включать",
221
+ }).then((result) => {
222
+ return result;
223
+ });
224
+ const res = await fetch("/api/bank/accounts", {
225
+ method: "POST",
226
+ headers: {
227
+ "Content-Type": "application/json",
228
+ "Bypass-Tunnel-Reminder": "true",
229
+ },
230
+ body: JSON.stringify({
231
+ pinCode: pin.value,
232
+ enableAccount2FA: fa.isConfirmed,
233
+ }),
234
+ });
235
+ const data = await res.json();
236
+ if (data.status) {
237
+ loadData();
238
+ Toastify({
239
+ text: `Вы создали щёт! \n Проверте "Мои щета"`,
240
+ duration: 3000,
241
+ close: true,
242
+ gravity: "top",
243
+ position: "right",
244
+ stopOnFocus: true,
245
+ style: {
246
+ background: "linear-gradient(to right, yellowgreen, green)",
247
+ },
248
+ }).showToast();
249
+ } else {
250
+ Toastify({
251
+ text: `cоздание щёта не удалось!`,
252
+ duration: 3000,
253
+ close: true,
254
+ style: { background: "RGB(245, 158, 11)" },
255
+ }).showToast();
256
+ console.error("Ошибка: " + data.message);
257
+ }
258
+ }
259
+ async function executeTransfer() {
260
+ const fromId = document.getElementById("tx-from").value;
261
+ const toId = document.getElementById("tx-to").value.trim();
262
+ const amount = document.getElementById("tx-amount").value;
263
+ const pinCode = document.getElementById("tx-pin").value;
264
+ const fa = await Swal.fire({
265
+ title: "Перевод",
266
+ text: "Вы подтверждаете перевод?",
267
+ icon: "question",
268
+ iconColor: "blue",
269
+ showCancelButton: true,
270
+ confirmButtonColor: "#3085d6",
271
+ cancelButtonColor: "#3085d6",
272
+ confirmButtonText: "Да, перевести",
273
+ cancelButtonText: "Нет, не переводить",
274
+ }).then((result) => {
275
+ return result;
276
+ });
277
+ if (!fa.isConfirmed) return;
278
+
279
+ const res = await fetch("/api/bank/accounts/transfer", {
280
+ method: "POST",
281
+ headers: {
282
+ "Content-Type": "application/json",
283
+ "Bypass-Tunnel-Reminder": "true",
284
+ },
285
+ body: JSON.stringify({ fromId, toId, amount, pinCode }),
286
+ });
287
+ const data = await res.json();
288
+ if (data.status) {
289
+ Toastify({
290
+ text: `Вы перевели ${amount} на другой щёт\n #${fromId} → #${toId}`,
291
+ duration: 3000,
292
+ style: { background: "rgb(58, 245, 11)" },
293
+ }).showToast();
294
+ loadData();
295
+ } else {
296
+ Toastify({
297
+ text: `Перевод средств не удался\n #${fromId} → #${toId}`,
298
+ duration: 3000,
299
+ style: { background: "RGB(245, 158, 11)" },
300
+ }).showToast();
301
+ console.error("Ошибка [" + data.statusCode + "]: " + data.message);
302
+ }
303
+ }
231
304
  async function applyForCredit() {
232
- const targetAccountId = document.getElementById('credit-target').value;
233
- const amount = document.getElementById('credit-amount').value;
234
- const paymentsCount = document.getElementById('credit-payments').value;
235
- const autoWithdrawal = document.getElementById('credit-auto').checked;
236
- const pinCode = document.getElementById('credit-pin').value;
237
- const res = await fetch('/api/bank/accounts/credit', {
238
- method: 'POST',
239
- headers: {'Content-Type': 'application/json'},
240
- body: JSON.stringify({
241
- accountId: targetAccountId,
242
- amount: parseFloat(amount),
243
- termMonths: parseInt(paymentsCount, 10),
244
- autoRepay: autoWithdrawal,
245
- pinCode: pinCode,
246
- rate: 0.1
247
- })
305
+ const targetAccountId = document.getElementById("credit-target").value;
306
+ const amount = document.getElementById("credit-amount").value;
307
+ const paymentsCount = document.getElementById("credit-payments").value;
308
+ const autoWithdrawal = document.getElementById("credit-auto").checked;
309
+ const pinCode = document.getElementById("credit-pin").value;
310
+ const res = await fetch("/api/bank/accounts/credit", {
311
+ method: "POST",
312
+ headers: {
313
+ "Content-Type": "application/json",
314
+ "Bypass-Tunnel-Reminder": "true",
315
+ },
316
+ body: JSON.stringify({
317
+ accountId: targetAccountId,
318
+ amount: parseFloat(amount),
319
+ termMonths: parseInt(paymentsCount, 10),
320
+ autoRepay: autoWithdrawal,
321
+ pinCode: pinCode,
322
+ rate: 0.1,
323
+ }),
324
+ });
325
+ const data = await res.json();
326
+ if (data.success || data.status) {
327
+ await Swal.fire({
328
+ title: "Кредит",
329
+ text:
330
+ "Кредит одобрен! Деньги зачислены на счет #" +
331
+ (data.creditId || data.creditAccountId),
332
+ icon: "success",
333
+ confirmButtonColor: "#3085d6",
334
+ confirmButtonText: "Ок",
248
335
  });
249
- const data = await res.json();
250
- if(data.success || data.status) {
251
- await Swal.fire({
252
- title: 'Кредит',
253
- text: "Кредит одобрен! Деньги зачислены на счет #" + (data.creditId || data.creditAccountId),
254
- icon: 'success',
255
- confirmButtonColor: '#3085d6',
256
- confirmButtonText: 'Ок'
257
- });
258
336
 
259
- Toastify({
260
- text: `Вы взяли кредит`,
261
- duration: 3000,
262
- style: { background: "rgb(58, 245, 11)" }
263
- }).showToast();
264
-
265
- if (typeof loadData === 'function') loadData();
266
- } else {
267
- await Swal.fire({
268
- title: 'Кредит',
269
- text: data.error || data.message || "Ошибка оформления кредита",
270
- icon: 'warning',
271
- iconColor: "orange",
272
- confirmButtonColor: '#3085d6',
273
- confirmButtonText: 'Ок'
274
- });
275
- console.error("Ошибка: " + (data.error || data.message));
276
- }
337
+ Toastify({
338
+ text: `Вы взяли кредит`,
339
+ duration: 3000,
340
+ style: { background: "rgb(58, 245, 11)" },
341
+ }).showToast();
342
+
343
+ if (typeof loadData === "function") loadData();
344
+ } else {
345
+ await Swal.fire({
346
+ title: "Кредит",
347
+ text: data.error || data.message || "Ошибка оформления кредита",
348
+ icon: "warning",
349
+ iconColor: "orange",
350
+ confirmButtonColor: "#3085d6",
351
+ confirmButtonText: "Ок",
352
+ });
353
+ console.error("Ошибка: " + (data.error || data.message));
354
+ }
277
355
  }
278
356
  document.getElementById("logoutBtn").addEventListener("click", async () => {
279
357
  const logOut = await Swal.fire({
280
- title: 'Выход',
281
- text: 'Вы подтверждаете выход из аккаунта?',
282
- icon: 'warning',
283
- iconColor: "orange",
284
- showCancelButton: true,
285
- confirmButtonColor: '#d63030',
286
- cancelButtonColor: '#46d630',
287
- confirmButtonText: 'Да, выйти',
288
- cancelButtonText: 'Отмена'
289
- }).then((result) => { return result; });
290
-
291
- if(!logOut.isConfirmed) return;
292
-
293
- await Toastify({ text: `Выход из аккаунта`, duration: 3000, style: { background: "rgb(245, 128, 11)" }}).showToast();
294
-
358
+ title: "Выход",
359
+ text: "Вы подтверждаете выход из аккаунта?",
360
+ icon: "warning",
361
+ iconColor: "orange",
362
+ showCancelButton: true,
363
+ confirmButtonColor: "#d63030",
364
+ cancelButtonColor: "#46d630",
365
+ confirmButtonText: "Да, выйти",
366
+ cancelButtonText: "Отмена",
367
+ }).then((result) => {
368
+ return result;
369
+ });
370
+
371
+ if (!logOut.isConfirmed) return;
372
+
373
+ await Toastify({
374
+ text: `Выход из аккаунта`,
375
+ duration: 3000,
376
+ style: { background: "rgb(245, 128, 11)" },
377
+ }).showToast();
378
+
295
379
  try {
296
- await fetch('/api/bank/users/logout', { method: 'POST' });
380
+ await fetch("/api/bank/users/logout", {
381
+ method: "POST",
382
+ "Bypass-Tunnel-Reminder": "true",
383
+ });
297
384
  } catch (e) {
298
- console.error('Ошибка сети при логауте:', e.message);
385
+ console.error("Ошибка сети при логауте:", e.message);
299
386
  }
300
- window.location.href = '/bank/home.html';
387
+ window.location.href = "/bank/home.html";
301
388
  });
302
- window.onload = loadData;
303
- let user2FaActive = false;
304
- async function syncProfileSectionData() {
305
- try {
306
- const response = await fetch('/api/bank/users/profile/settings');
307
- if (!response.ok) {
308
- console.warn("⚠️ [City-bank] Не удалось загрузить данные профиля, сервер вернул статус:", response.status);
309
- return;
389
+ window.onload = loadData;
390
+ let user2FaActive = false;
391
+ async function syncProfileSectionData() {
392
+ try {
393
+ const response = await fetch("/api/bank/users/profile/settings", {
394
+ headers: {
395
+ "Bypass-Tunnel-Reminder": "true",
396
+ },
397
+ });
398
+ if (!response.ok) {
399
+ console.warn(
400
+ "⚠️ [City-bank] Не удалось загрузить данные профиля, сервер вернул статус:",
401
+ response.status,
402
+ );
403
+ return;
404
+ }
405
+ const result = await response.json();
406
+ if (result.status && result.user) {
407
+ if (result.user.avatar) {
408
+ document.getElementById("user-avatar").src = result.user.avatar;
310
409
  }
311
- const result = await response.json();
312
- if (result.status && result.user) {
313
- if (result.user.avatar) {
314
- document.getElementById('user-avatar').src = result.user.avatar;
315
- }
316
- user2FaActive = result.user.two_fa_status;
317
- const statusText = document.getElementById('2fa-current-status');
318
- const toggleBtn = document.getElementById('btn-toggle-2fa');
319
-
320
- if (user2FaActive) {
321
- statusText.innerHTML = 'Текущий статус: <span style="color:green; font-weight:bold;">ЗАЩИЩЕН (2FA включена)</span>';
322
- toggleBtn.innerText = 'Отключить защиту 2FA';
323
- toggleBtn.className = 'btn btn-danger';
324
- } else {
325
- statusText.innerHTML = 'Текущий статус: <span style="color:red; font-weight:bold;">НЕБЕЗОПАСНО (2FA выключена)</span>';
326
- toggleBtn.innerText = 'Включить защиту 2FA';
327
- toggleBtn.className = 'btn';
328
- }
410
+ user2FaActive = result.user.two_fa_status;
411
+ const statusText = document.getElementById("2fa-current-status");
412
+ const toggleBtn = document.getElementById("btn-toggle-2fa");
413
+
414
+ if (user2FaActive) {
415
+ statusText.innerHTML =
416
+ 'Текущий статус: <span style="color:green; font-weight:bold;">ЗАЩИЩЕН (2FA включена)</span>';
417
+ toggleBtn.innerText = "Отключить защиту 2FA";
418
+ toggleBtn.className = "btn btn-danger";
419
+ } else {
420
+ statusText.innerHTML =
421
+ 'Текущий статус: <span style="color:red; font-weight:bold;">НЕБЕЗОПАСНО (2FA выключена)</span>';
422
+ toggleBtn.innerText = "Включить защиту 2FA";
423
+ toggleBtn.className = "btn";
329
424
  }
330
- } catch (e) {
331
- console.error("Ошибка парсинга или запроса профиля:", e);
332
425
  }
426
+ } catch (e) {
427
+ console.error("Ошибка парсинга или запроса профиля:", e);
428
+ }
429
+ }
430
+ async function uploadAvatar() {
431
+ const fileInput = document.getElementById("avatar-file");
432
+ if (fileInput.files.length === 0) {
433
+ await Swal.fire({
434
+ title: "Изменение аватарки",
435
+ text: "Пожалуйста, выберите файл изображения",
436
+ icon: "warning",
437
+ iconColor: "orange",
438
+ confirmButtonColor: "#3085d6",
439
+ confirmButtonText: "Ок",
440
+ }).then((result) => {
441
+ return result;
442
+ });
443
+ return;
333
444
  }
334
- async function uploadAvatar() {
335
- const fileInput = document.getElementById('avatar-file');
336
- if (fileInput.files.length === 0) {
337
- await Swal.fire({title: 'Изменение аватарки', text: 'Пожалуйста, выберите файл изображения', icon: 'warning', iconColor:"orange", confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'}).then((result) => {return result;});
338
- return;
339
- }
340
445
 
341
- const formData = new FormData();
342
- formData.append('avatar', fileInput.files[0]);
446
+ const formData = new FormData();
447
+ formData.append("avatar", fileInput.files[0]);
343
448
 
344
- try {
345
- const res = await fetch('/api/bank/users/avatar', {
346
- method: 'POST',
347
- body: formData
348
- });
349
- const data = await res.json();
449
+ try {
450
+ const res = await fetch("/api/bank/users/avatar", {
451
+ method: "POST",
452
+ headers: {
453
+ "Bypass-Tunnel-Reminder": "true",
454
+ },
455
+ body: formData,
456
+ });
457
+ const data = await res.json();
350
458
 
351
- if (data.status) {
352
- await Swal.fire({title: 'Изменение аватарки', text: 'Аватарка успешно обновлена', icon: 'sucess', confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'});
353
- Toastify({
354
- text: `Вы изменили аватарку`,
355
- duration:3000,
356
- style: { background: "rgb(58, 245, 11)" }
357
- }).showToast();
358
- document.getElementById('user-avatar').src = data.avatar;
359
- fileInput.value = "";
360
- } else {
361
- await Swal.fire({title: 'Изменение аватарки', text: 'Ошибка', icon: 'warning', iconColor:"orange", confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'});
362
- console.log("Ошибка: " + data.message);
363
- }
364
- } catch (err) {
365
- await Swal.fire({title: 'Изменение аватарки', text: 'Ошибка. Не удалось загрузить файл на сервер', icon: 'error', confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'});
366
- console.error(err);
459
+ if (data.status) {
460
+ await Swal.fire({
461
+ title: "Изменение аватарки",
462
+ text: "Аватарка успешно обновлена",
463
+ icon: "sucess",
464
+ confirmButtonColor: "#3085d6",
465
+ confirmButtonText: "Ок",
466
+ });
467
+ Toastify({
468
+ text: `Вы изменили аватарку`,
469
+ duration: 3000,
470
+ style: { background: "rgb(58, 245, 11)" },
471
+ }).showToast();
472
+ document.getElementById("user-avatar").src = data.avatar;
473
+ fileInput.value = "";
474
+ } else {
475
+ await Swal.fire({
476
+ title: "Изменение аватарки",
477
+ text: "Ошибка",
478
+ icon: "warning",
479
+ iconColor: "orange",
480
+ confirmButtonColor: "#3085d6",
481
+ confirmButtonText: "Ок",
482
+ });
483
+ console.log("Ошибка: " + data.message);
367
484
  }
485
+ } catch (err) {
486
+ await Swal.fire({
487
+ title: "Изменение аватарки",
488
+ text: "Ошибка. Не удалось загрузить файл на сервер",
489
+ icon: "error",
490
+ confirmButtonColor: "#3085d6",
491
+ confirmButtonText: "Ок",
492
+ });
493
+ console.error(err);
368
494
  }
369
- async function updatePassword() {
370
- const oldPassword = document.getElementById('p-old').value;
371
- const newPassword = document.getElementById('p-new').value;
372
- if (!oldPassword || !newPassword) {
373
- const t = await Swal.fire({title: 'Изменение пароля', text: 'Заполните оба поля паролей', icon: 'warning', iconColor:"orange", confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'}).then((result) => {return result;});
374
- return;
375
- }
376
- if (user2FaActive) {
377
- const { value: totpCode } = await Swal.fire({title: "Изменение пароля", text:"Введите ваш 2FA токен подтверждения для смены пароля", icon:"question", iconColor:"blue", input: "password", inputLabel: "Password", inputPlaceholder: "Введите ваш 2FA токен", inputAttributes: {maxlength: "10", autocapitalize: "off", autocorrect: "off"}});
378
- if (!totpCode) return;
379
- }
380
- try {
381
- const res = await fetch('/api/bank/users/change-password', {
382
- method: 'POST',
383
- headers: {'Content-Type': 'application/json'},
384
- body: JSON.stringify({ oldPassword, newPassword, totpCode })
495
+ }
496
+ async function updatePassword() {
497
+ const oldPassword = document.getElementById("p-old").value;
498
+ const newPassword = document.getElementById("p-new").value;
499
+ if (!oldPassword || !newPassword) {
500
+ const t = await Swal.fire({
501
+ title: "Изменение пароля",
502
+ text: "Заполните оба поля паролей",
503
+ icon: "warning",
504
+ iconColor: "orange",
505
+ confirmButtonColor: "#3085d6",
506
+ confirmButtonText: "Ок",
507
+ }).then((result) => {
508
+ return result;
509
+ });
510
+ return;
511
+ }
512
+ if (user2FaActive) {
513
+ const { value: totpCode } = await Swal.fire({
514
+ title: "Изменение пароля",
515
+ text: "Введите ваш 2FA токен подтверждения для смены пароля",
516
+ icon: "question",
517
+ iconColor: "blue",
518
+ input: "password",
519
+ inputLabel: "Password",
520
+ inputPlaceholder: "Введите ваш 2FA токен",
521
+ inputAttributes: {
522
+ maxlength: "10",
523
+ autocapitalize: "off",
524
+ autocorrect: "off",
525
+ },
526
+ });
527
+ if (!totpCode) return;
528
+ }
529
+ try {
530
+ const res = await fetch("/api/bank/users/change-password", {
531
+ method: "POST",
532
+ headers: {
533
+ "Content-Type": "application/json",
534
+ "Bypass-Tunnel-Reminder": "true",
535
+ },
536
+ body: JSON.stringify({ oldPassword, newPassword, totpCode }),
537
+ });
538
+ const data = await res.json();
539
+ if (data.status) {
540
+ await Swal.fire({
541
+ title: "Изменение пароля",
542
+ text: "Пароль успешно изменен",
543
+ icon: "sucess",
544
+ confirmButtonColor: "#3085d6",
545
+ confirmButtonText: "Ок",
546
+ });
547
+ document.getElementById("p-old").value = "";
548
+ document.getElementById("p-new").value = "";
549
+ Toastify({
550
+ text: `Вы изменили пароль`,
551
+ duration: 3000,
552
+ style: { background: "rgb(58, 245, 11)" },
553
+ }).showToast();
554
+ } else {
555
+ console.error("Ошибка изменения пароля. Код [" + data.statusCode + "]");
556
+ await Swal.fire({
557
+ title: "Изменение пароля",
558
+ text: "Ошибка изменения пароля",
559
+ icon: "warning",
560
+ iconColor: "orange",
561
+ confirmButtonColor: "#3085d6",
562
+ confirmButtonText: "Ок",
385
563
  });
386
- const data = await res.json();
387
- if (data.status) {
388
- await Swal.fire({title:"Изменение пароля", text:"Пароль успешно изменен", icon:"sucess", confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'})
389
- document.getElementById('p-old').value = "";
390
- document.getElementById('p-new').value = "";
391
- Toastify({
392
- text: `Вы изменили пароль`,
393
- duration:3000,
394
- style: { background: "rgb(58, 245, 11)" }
395
- }).showToast();
396
- } else {
397
- console.error("Ошибка изменения пароля. Код [" + data.statusCode + "]");
398
- await Swal.fire({title:"Изменение пароля", text:"Ошибка изменения пароля", icon:"warning", iconColor:"orange", confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'})
399
- }
400
- } catch (err) {
401
- console.error(err);
402
564
  }
565
+ } catch (err) {
566
+ console.error(err);
567
+ }
568
+ }
569
+ async function updateGmail() {
570
+ const newGmail = document.getElementById("profile-new-gmail").value.trim();
571
+ if (!newGmail || !newGmail.includes("@")) {
572
+ await Swal.fire({
573
+ title: "Изменение почты",
574
+ text: "Введите корректный адрес почты!",
575
+ icon: "warning",
576
+ iconColor: "orange",
577
+ confirmButtonColor: "#3085d6",
578
+ confirmButtonText: "Ок",
579
+ }).then((result) => {
580
+ return result;
581
+ });
582
+ return;
403
583
  }
404
- async function updateGmail() {
405
- const newGmail = document.getElementById('profile-new-gmail').value.trim();
406
- if (!newGmail || !newGmail.includes('@')) {
407
- await Swal.fire({title: 'Изменение почты', text: 'Введите корректный адрес почты!', icon: 'warning', iconColor:"orange", confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'}).then((result) => {return result;});
408
- return;
409
- }
410
584
 
411
- try {
412
- const res = await fetch('/api/bank/users/change-gmail', {
413
- method: 'POST',
414
- headers: {'Content-Type': 'application/json'},
415
- body: JSON.stringify({ newGmail })
416
- });
417
- const data = await res.json();
585
+ try {
586
+ const res = await fetch("/api/bank/users/change-gmail", {
587
+ method: "POST",
588
+ headers: {
589
+ "Content-Type": "application/json",
590
+ "Bypass-Tunnel-Reminder": "true",
591
+ },
592
+ body: JSON.stringify({ newGmail }),
593
+ });
594
+ const data = await res.json();
418
595
 
419
- if (data.status) {
420
- await Swal.fire({title: 'Изменение почты', text: 'Адрес Google почты успешно обновлен!', icon: 'sucess', confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'}).then((result) => {return result;});
421
- Toastify({
422
- text: `Вы изменили адрес элекстронной почты`,
423
- duration:3000,
424
- style: { background: "rgb(15, 245, 11)" }
425
- }).showToast();
426
- console.log("Адрес Google почты успешно обновлен!");
427
- document.getElementById('profile-new-gmail').value = "";
428
- } else {
429
- await Swal.fire({title: 'Изменение почты', text: 'Ошибка смены почты', icon: 'warning', iconColor:"orange", confirmButtonColor: '#3085d6', confirmButtonText: 'Ок'}).then((result) => {return result;});
430
- console.log("Ошибка смены почты: Код [" + data.statusCode + "]");
431
- }
432
- } catch (err) {
433
- console.error(err);
596
+ if (data.status) {
597
+ await Swal.fire({
598
+ title: "Изменение почты",
599
+ text: "Адрес Google почты успешно обновлен!",
600
+ icon: "sucess",
601
+ confirmButtonColor: "#3085d6",
602
+ confirmButtonText: "Ок",
603
+ }).then((result) => {
604
+ return result;
605
+ });
606
+ Toastify({
607
+ text: `Вы изменили адрес элекстронной почты`,
608
+ duration: 3000,
609
+ style: { background: "rgb(15, 245, 11)" },
610
+ }).showToast();
611
+ console.log("Адрес Google почты успешно обновлен!");
612
+ document.getElementById("profile-new-gmail").value = "";
613
+ } else {
614
+ await Swal.fire({
615
+ title: "Изменение почты",
616
+ text: "Ошибка смены почты",
617
+ icon: "warning",
618
+ iconColor: "orange",
619
+ confirmButtonColor: "#3085d6",
620
+ confirmButtonText: "Ок",
621
+ }).then((result) => {
622
+ return result;
623
+ });
624
+ console.log("Ошибка смены почты: Код [" + data.statusCode + "]");
434
625
  }
626
+ } catch (err) {
627
+ console.error(err);
435
628
  }
436
- async function toggleSystem2FA() {
437
- const url = user2FaActive ? '/api/bank/users/disable-2fa' : '/api/bank/users/enable-2fa';
438
- try {
439
- const res = await fetch(url, { method: 'POST' });
440
- const data = await res.json();
441
- if (data.status) {
442
- if (!user2FaActive && data.secret2FA) {
443
- document.getElementById('profile-2fa-setup').style.display = 'block';
444
- document.getElementById('profile-2fa-key').innerText = data.secret2FA;
445
- Toastify({
446
- text: `Защита 2FA успешно активирована`,
447
- duration:3000,
448
- style: { background: "rgb(31, 245, 11)" }
449
- }).showToast();
450
- } else {
451
- document.getElementById('profile-2fa-setup').style.display = 'none';
452
- Toastify({
453
- text: `Защита 2FA успешно отключена`,
454
- duration:3000,
455
- style: { background: "rgb(245, 171, 11)" }
456
- }).showToast();
457
- }
458
- syncProfileSectionData();
629
+ }
630
+ async function toggleSystem2FA() {
631
+ const url = user2FaActive
632
+ ? "/api/bank/users/disable-2fa"
633
+ : "/api/bank/users/enable-2fa";
634
+ try {
635
+ const res = await fetch(url, {
636
+ method: "POST",
637
+ headers: {
638
+ "Bypass-Tunnel-Reminder": "true",
639
+ },
640
+ });
641
+ const data = await res.json();
642
+ if (data.status) {
643
+ if (!user2FaActive && data.secret2FA) {
644
+ document.getElementById("profile-2fa-setup").style.display = "block";
645
+ document.getElementById("profile-2fa-key").innerText = data.secret2FA;
646
+ Toastify({
647
+ text: `Защита 2FA успешно активирована`,
648
+ duration: 3000,
649
+ style: { background: "rgb(31, 245, 11)" },
650
+ }).showToast();
459
651
  } else {
652
+ document.getElementById("profile-2fa-setup").style.display = "none";
460
653
  Toastify({
461
- text: `Ошибка управления 2FA`,
462
- duration:3000,
463
- style: { background: "rgb(245, 11, 11)" }
654
+ text: `Защита 2FA успешно отключена`,
655
+ duration: 3000,
656
+ style: { background: "rgb(245, 171, 11)" },
464
657
  }).showToast();
465
658
  }
466
- } catch (err) {
467
- console.error(err);
659
+ syncProfileSectionData();
660
+ } else {
661
+ Toastify({
662
+ text: `Ошибка управления 2FA`,
663
+ duration: 3000,
664
+ style: { background: "rgb(245, 11, 11)" },
665
+ }).showToast();
468
666
  }
667
+ } catch (err) {
668
+ console.error(err);
469
669
  }
470
- async function closeAccount(accountId) {
471
- const deleteAccountStatus = await Swal.fire({title: 'Удаление щёта', text: 'Вы действительно хотите удалить щёт?', icon: 'warning', iconColor:"orange", showCancelButton: true, confirmButtonColor: '#d63030', cancelButtonColor: '#46d630', confirmButtonText: 'Да, удалить', cancelButtonText: 'Отмена'}).then((result) => {return result;});
472
- if (!deleteAccountStatus.isConfirmed) return;
473
- try {
474
- const response = await fetch(`/api/bank/accounts/${accountId}`, {
475
- method: 'DELETE'
670
+ }
671
+ async function closeAccount(accountId) {
672
+ const deleteAccountStatus = await Swal.fire({
673
+ title: "Удаление щёта",
674
+ text: "Вы действительно хотите удалить щёт?",
675
+ icon: "warning",
676
+ iconColor: "orange",
677
+ showCancelButton: true,
678
+ confirmButtonColor: "#d63030",
679
+ cancelButtonColor: "#46d630",
680
+ confirmButtonText: "Да, удалить",
681
+ cancelButtonText: "Отмена",
682
+ }).then((result) => {
683
+ return result;
684
+ });
685
+ if (!deleteAccountStatus.isConfirmed) return;
686
+ try {
687
+ const response = await fetch(`/api/bank/accounts/${accountId}`, {
688
+ method: "DELETE",
689
+ headers: {
690
+ "Bypass-Tunnel-Reminder": "true",
691
+ },
692
+ });
693
+ if (!response.ok) {
694
+ const errorText = await response.text();
695
+ console.error("Критическая ошибка бэкенда:", errorText);
696
+ await Swal.fire({
697
+ title: "Удаление щёта",
698
+ text: "Ошибка при удалении щета. Попробуйте позже...",
699
+ icon: "error",
700
+ iconColor: "orange",
701
+ showCancelButton: true,
702
+ confirmButtonColor: "#307bd6",
703
+ confirmButtonText: "Ок",
704
+ }).then((result) => {
705
+ return result;
476
706
  });
477
- if (!response.ok) {
478
- const errorText = await response.text();
479
- console.error("Критическая ошибка бэкенда:", errorText);
480
- await Swal.fire({title: 'Удаление щёта', text:'Ошибка при удалении щета. Попробуйте позже...', icon: 'error', iconColor:"orange", showCancelButton: true, confirmButtonColor: '#307bd6', confirmButtonText: 'Ок'}).then((result) => {return result;});
481
- return;
482
- }
483
- const data = await response.json();
484
- if (data.status) {
485
- Toastify({
486
- text: `Щёт успешно удалён`,
487
- duration:3000,
488
- style: { background: "rgb(46, 245, 11)" }
489
- }).showToast();
490
- loadData();
491
- } else {
492
- Toastify({
493
- text: `Ошибка удаления`,
494
- duration:3000,
495
- style: { background: "rgb(245, 187, 11)" }
496
- }).showToast();
497
- await Swal.fire({title: 'Удаление щёта', text:'Ошибка при удалении щета. Счет должен быть пуст и разблокирован', icon: 'information', iconColor:"orange", showCancelButton: true, confirmButtonColor: '#307bd6', confirmButtonText: 'Ок'}).then((result) => {return result;});
498
- }
499
- } catch (err) {
500
- console.error("Ошибка при закрытии счета:", err);
707
+ return;
708
+ }
709
+ const data = await response.json();
710
+ if (data.status) {
711
+ Toastify({
712
+ text: `Щёт успешно удалён`,
713
+ duration: 3000,
714
+ style: { background: "rgb(46, 245, 11)" },
715
+ }).showToast();
716
+ loadData();
717
+ } else {
501
718
  Toastify({
502
719
  text: `Ошибка удаления`,
503
- duration:3000,
504
- style: { background: "rgb(245, 11, 11)" }
720
+ duration: 3000,
721
+ style: { background: "rgb(245, 187, 11)" },
505
722
  }).showToast();
723
+ await Swal.fire({
724
+ title: "Удаление щёта",
725
+ text: "Ошибка при удалении щета. Счет должен быть пуст и разблокирован",
726
+ icon: "information",
727
+ iconColor: "orange",
728
+ showCancelButton: true,
729
+ confirmButtonColor: "#307bd6",
730
+ confirmButtonText: "Ок",
731
+ }).then((result) => {
732
+ return result;
733
+ });
506
734
  }
735
+ } catch (err) {
736
+ console.error("Ошибка при закрытии счета:", err);
737
+ Toastify({
738
+ text: `Ошибка удаления`,
739
+ duration: 3000,
740
+ style: { background: "rgb(245, 11, 11)" },
741
+ }).showToast();
507
742
  }
508
- async function loadTransactionLogs() {
509
- try {
510
- const response = await fetch('/api/bank/accounts/my-logs');
511
- if (!response.ok) return;
512
- const result = await response.json();
513
- const tbody = document.querySelector('#table-logs tbody');
514
- tbody.innerHTML = "";
515
- if (result.logs.length === 0) {
516
- tbody.innerHTML = `<tr><td colspan="5" style="text-align:center; color:#aaa;">У вас пока нет совершенных транзакций</td></tr>`;
517
- return;
743
+ }
744
+ async function loadTransactionLogs() {
745
+ try {
746
+ const response = await fetch("/api/bank/accounts/my-logs", {
747
+ headers: {
748
+ "Bypass-Tunnel-Reminder": "true",
749
+ },
750
+ });
751
+ if (!response.ok) return;
752
+ const result = await response.json();
753
+ const tbody = document.querySelector("#table-logs tbody");
754
+ tbody.innerHTML = "";
755
+ if (result.logs.length === 0) {
756
+ tbody.innerHTML = `<tr><td colspan="5" style="text-align:center; color:#aaa;">У вас пока нет совершенных транзакций</td></tr>`;
757
+ return;
758
+ }
759
+ result.logs.forEach((log) => {
760
+ const tad = new Date(log.createdAt);
761
+ const date = `${tad.getFullYear()}.${tad.getMonth()}.${tad.getDay()}`;
762
+ const time = `${tad.getHours()}:${tad.getMinutes()}:${tad.getSeconds()}`;
763
+ let typeText = "";
764
+ let amountStyle = "";
765
+ let prefix = "";
766
+ let sender = "";
767
+ if (log.type === "DEPOSIT" || log.type === "TRANSFER_IN") {
768
+ typeText = log.type === "DEPOSIT" ? "Пополнение" : "Перевод (Входящий)";
769
+ amountStyle = "color: green; font-weight: bold;";
770
+ prefix = "+";
771
+ } else {
772
+ typeText = log.type === "WITHDRAW" ? "Списание" : "Перевод (Исходящий)";
773
+ amountStyle = "color: red; font-weight: bold;";
774
+ prefix = "-";
518
775
  }
519
- result.logs.forEach(log => {
520
- const tad = new Date(log.createdAt);
521
- const date = `${tad.getFullYear()}.${tad.getMonth()}.${tad.getDay()}`;
522
- const time = `${tad.getHours()}:${tad.getMinutes()}:${tad.getSeconds()}`;
523
- let typeText = "";
524
- let amountStyle = "";
525
- let prefix = "";
526
- let sender = "";
527
- if (log.type === 'DEPOSIT' || log.type === 'TRANSFER_IN') {
528
- typeText = log.type === 'DEPOSIT' ? 'Пополнение' : 'Перевод (Входящий)';
529
- amountStyle = 'color: green; font-weight: bold;';
530
- prefix = "+";
531
- } else {
532
- typeText = log.type === 'WITHDRAW' ? 'Списание' : 'Перевод (Исходящий)';
533
- amountStyle = 'color: red; font-weight: bold;';
534
- prefix = "-";
535
- }
536
- if(log.sender_or_receiver == "Администрация (ROOT)"){
537
- sender = `<span style='color:blue' id='root001'><strong><i>Администрация (ROOT)</i></strong></span>`
538
- } else {
539
- sender = log.sender_or_receiver;
540
- }
541
- tbody.innerHTML += `
776
+ if (log.sender_or_receiver == "Администрация (ROOT)") {
777
+ sender = `<span style='color:blue' id='root001'><strong><i>Администрация (ROOT)</i></strong></span>`;
778
+ } else {
779
+ sender = log.sender_or_receiver;
780
+ }
781
+ tbody.innerHTML += `
542
782
  <tr>
543
783
  <td>${date}</td>
544
784
  <td>${time}</td>
545
785
  <td><b>#${log.account_id}</b></td>
546
786
  <td>${typeText}</td>
547
- <td>${sender || '—'}</td>
787
+ <td>${sender || "—"}</td>
548
788
  <td style="${amountStyle}">${prefix} ${parseFloat(log.amount).toFixed(2)} руб.</td>
549
789
  </tr>
550
790
  `;
551
- });
552
- } catch (err) {
553
- console.error("Ошибка загрузки истории операций:", err);
554
- }
791
+ });
792
+ } catch (err) {
793
+ console.error("Ошибка загрузки истории операций:", err);
555
794
  }
556
- async function executeCreateCredit() {
795
+ }
796
+ async function executeCreateCredit() {
557
797
  try {
558
- // 1. Извлекаем элементы DOM
559
- const accountIdField = document.getElementById('credit-accountId');
560
- const amountField = document.getElementById('credit-amount');
561
- const autoRepayField = document.getElementById('credit-autoRepay');
562
- const rateField = document.getElementById('credit-rate');
563
- const termMonthsField = document.getElementById('credit-termMonths');
564
-
565
- // Проверка на случай, если элементы не найдены в DOM (чтобы скрипт не падал)
798
+ const accountIdField = document.getElementById("credit-accountId");
799
+ const amountField = document.getElementById("credit-amount");
800
+ const autoRepayField = document.getElementById("credit-autoRepay");
801
+ const rateField = document.getElementById("credit-rate");
802
+ const termMonthsField = document.getElementById("credit-termMonths");
566
803
  if (!accountIdField || !amountField) {
567
- console.error('Критические элементы формы кредита не найдены в HTML дерева.');
804
+ console.error(
805
+ "Критические элементы формы кредита не найдены в HTML дерева.",
806
+ );
568
807
  return;
569
808
  }
570
-
571
- // 2. Формируем чистые переменные
572
809
  const accountId = accountIdField.value;
573
810
  const amount = parseFloat(amountField.value);
574
- const autoRepay = autoRepayField ? autoRepayField.checked : false; // Чекбоксы проверяются через .checked
811
+ const autoRepay = autoRepayField ? autoRepayField.checked : false;
575
812
  const rate = rateField ? parseFloat(rateField.value) : 0.1;
576
- const termMonths = termMonthsField ? parseInt(termMonthsField.value, 10) : 12;
577
-
578
- // Простая клиентская валидация перед отправкой
813
+ const termMonths = termMonthsField
814
+ ? parseInt(termMonthsField.value, 10)
815
+ : 12;
579
816
  if (!accountId || isNaN(amount) || amount <= 0) {
580
- Swal.fire('Внимание', 'Пожалуйста, заполните номер счета и корректную сумму', 'warning');
817
+ Swal.fire(
818
+ "Внимание",
819
+ "Пожалуйста, заполните номер счета и корректную сумму",
820
+ "warning",
821
+ );
581
822
  return;
582
823
  }
583
-
584
- // 3. Отправляем fetch-запрос на бэкенд-роут игрового банка
585
- const response = await fetch('/api/bank/accounts/credit', {
586
- method: 'POST',
587
- headers: {
588
- 'Content-Type': 'application/json'
589
- // Если вы используете JWT или сессии, сюда может потребоваться передать токен авторизации
824
+ const response = await fetch("/api/bank/accounts/credit", {
825
+ method: "POST",
826
+ headers: {
827
+ "Content-Type": "application/json",
828
+ "Bypass-Tunnel-Reminder": "true",
590
829
  },
591
830
  body: JSON.stringify({
592
831
  accountId,
593
832
  amount,
594
833
  autoRepay,
595
834
  rate,
596
- termMonths
597
- })
835
+ termMonths,
836
+ }),
598
837
  });
599
-
600
838
  if (!response.ok) {
601
839
  throw new Error(`Ошибка сервера: ${response.status}`);
602
840
  }
603
-
604
841
  const result = await response.json();
605
-
606
- // 4. Обработка ответа от бэкенда
607
842
  if (result.success) {
608
- Swal.fire('Успех!', result.message || 'Кредит успешно оформлен', 'success').then(() => {
609
- // Перенаправляем пользователя на страницу счетов, чтобы увидеть обновленный баланс
610
- location.href = '/bank/accounts.html';
843
+ Swal.fire(
844
+ "Успех!",
845
+ result.message || "Кредит успешно оформлен",
846
+ "success",
847
+ ).then(() => {
848
+ location.href = "/bank/accounts.html";
611
849
  });
612
850
  } else {
613
- Swal.fire('Ошибка оформления', result.error || 'Не удалось выдать кредит', 'error');
851
+ Swal.fire(
852
+ "Ошибка оформления",
853
+ result.error || "Не удалось выдать кредит",
854
+ "error",
855
+ );
614
856
  }
615
-
616
857
  } catch (error) {
617
- console.error('Ошибка выполнения executeCreateCredit:', error);
618
- Swal.fire('Критическая ошибка', 'Не удалось связаться с сервером банка', 'error');
858
+ console.error("Ошибка выполнения executeCreateCredit:", error);
859
+ Swal.fire(
860
+ "Критическая ошибка",
861
+ "Не удалось связаться с сервером банка",
862
+ "error",
863
+ );
619
864
  }
620
865
  }