sequelize-game-bank 0.0.1 → 1.0.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.
Files changed (81) hide show
  1. package/.env +114 -0
  2. package/database/citybank.db +0 -0
  3. package/database/config/init-db.js +33 -0
  4. package/database/models/Account.js +44 -0
  5. package/database/models/Credit.js +39 -0
  6. package/database/models/Transaction.js +27 -0
  7. package/database/models/User.js +50 -0
  8. package/database/services/account/add-manny.js +19 -0
  9. package/database/services/account/change-pincode.js +23 -0
  10. package/database/services/account/change-status.js +19 -0
  11. package/database/services/account/create-credit.js +106 -0
  12. package/database/services/account/create.js +24 -0
  13. package/database/services/account/delete.js +23 -0
  14. package/database/services/account/find-manny.js +12 -0
  15. package/database/services/account/logs.js +26 -0
  16. package/database/services/account/remove-manny.js +18 -0
  17. package/database/services/account/transfer.js +83 -0
  18. package/database/services/accounts.js +13 -0
  19. package/database/services/bot/bot.js +38 -0
  20. package/database/services/bot/generators/ban-bot.html +1 -0
  21. package/database/services/bot/generators/ban-bot.txt +1 -0
  22. package/database/services/bot/generators/disable-2FA-bot.html +1 -0
  23. package/database/services/bot/generators/disable-2FA-bot.txt +1 -0
  24. package/database/services/bot/generators/enable-2FA-bot.html +1 -0
  25. package/database/services/bot/generators/enable-2FA-bot.txt +1 -0
  26. package/database/services/bot/generators/login-bot.html +1 -0
  27. package/database/services/bot/generators/login-bot.txt +1 -0
  28. package/database/services/bot/generators/register-bot.html +1 -0
  29. package/database/services/bot/generators/register-bot.txt +7 -0
  30. package/database/services/bot/generators/transfer-from-bot.html +1 -0
  31. package/database/services/bot/generators/transfer-from-bot.txt +1 -0
  32. package/database/services/bot/generators/transfer-to-bot.html +1 -0
  33. package/database/services/bot/generators/transfer-to-bot.txt +1 -0
  34. package/database/services/bot/generators/unban-bot.html +1 -0
  35. package/database/services/bot/generators/unban-bot.txt +1 -0
  36. package/database/services/bot/write.js +49 -0
  37. package/database/services/user/change-avatar.js +16 -0
  38. package/database/services/user/change-gmail.js +13 -0
  39. package/database/services/user/change-password.js +16 -0
  40. package/database/services/user/create.js +56 -0
  41. package/database/services/user/delete.js +15 -0
  42. package/database/services/user/disable-2FA.js +16 -0
  43. package/database/services/user/enable-2FA.js +19 -0
  44. package/database/services/user/login.js +51 -0
  45. package/database/services/user/math-manny.js +8 -0
  46. package/database/services/users.js +11 -0
  47. package/index.js +83 -0
  48. package/logs/errors.log +14 -0
  49. package/package.json +29 -13
  50. package/public/accounts.html +44 -0
  51. package/public/admin.html +194 -0
  52. package/public/credit.html +61 -0
  53. package/public/css/style.css +58 -0
  54. package/public/general.html +53 -0
  55. package/public/history.html +62 -0
  56. package/public/home.html +378 -0
  57. package/public/icon/icon.png +0 -0
  58. package/public/js/example.html +559 -0
  59. package/public/js/js.js +482 -0
  60. package/public/js/sweetalert2.js +2 -0
  61. package/public/login.html +72 -0
  62. package/public/profile.html +78 -0
  63. package/public/register.html +442 -0
  64. package/public/transfer.html +57 -0
  65. package/public/uploads/avatars/Pavel_1790269874496.png +0 -0
  66. package/public/uploads/avatars/Pavel_1790275457821.png +0 -0
  67. package/public/uploads/avatars/Pavel_1790867570006.png +0 -0
  68. package/public/uploads/avatars/Romanenko_1790270954677.png +0 -0
  69. package/public/uploads/avatars/default.png +0 -0
  70. package/public/uploads/avatars/root_1790275537324.png +0 -0
  71. package/public/uploads/avatars/root_1790275537381.png +0 -0
  72. package/public/uploads/avatars/root_1790349234549.png +0 -0
  73. package/public/uploads/avatars/root_1790519734671.png +0 -0
  74. package/public/uploads/avatars/root_1790879784620.png +0 -0
  75. package/public/uploads/avatars/root_1790879790948.png +0 -0
  76. package/public/uploads/avatars/root_1790879796755.png +0 -0
  77. package/public/uploads/avatars/root_1790962800544.png +0 -0
  78. package/public/uploads/avatars/root_1791009911239.png +0 -0
  79. package/server/middleware/auth.js +34 -0
  80. package/server/server.js +235 -0
  81. package/test.js +20 -0
package/.env ADDED
@@ -0,0 +1,114 @@
1
+ PORT=3000
2
+ JWT_SECRET=0000
3
+ ROOT_PASSWORD=bank-root
4
+ GMAIL=p7841744@gmail.com
5
+ GOOGLE_APP_PASSWORD=pqts sqeg fvgk qizq
6
+
7
+
8
+
9
+
10
+ # ==============================================================================
11
+ # 🎨 CUSTOM-COLOR-LOGS CONFIGURATION (v3.0.0)
12
+ # ==============================================================================
13
+ CUSTOM_COLOR_LOGS_INITIALIZED = true
14
+
15
+ # ⚙️ ОСНОВНЫЕ СИСТЕМНЫЕ НАСТРОЙКИ
16
+ # ------------------------------------------------------------------------------
17
+ SHOW_START_LOG = true
18
+ SHOW_MODULE_LOGS = true
19
+ SHOW_END_LOG = true
20
+ SHOW_TIME_AT_LOG = true
21
+
22
+ # 🧩 СИСТЕМНЫЕ СВЯЗУЮЩИЕ ЗНАКИ И ЦВЕТА
23
+ # ------------------------------------------------------------------------------
24
+ AND = " "
25
+ AND_COLOR = Black
26
+ TO = ":"
27
+ TO_COLOR = Blue
28
+
29
+ # 🖌️ НАСТРОЙКА УМНОГО ОКРАШИВАНИЯ СТРОК И ОБЪЕКТОВ (ФИЧА v3.0.0)
30
+ # ------------------------------------------------------------------------------
31
+ COLOR = Gray # Цвет для обычного Info текста
32
+ TIME_COLOR = White # Цвет времени [12:00:00]
33
+ KEY_COLOR = Magenta # Цвет ключей объектов (например, name, id)
34
+ VALUE_COLOR = Yellow # Цвет значений (например, "Иван", 777)
35
+
36
+ # 📢 ГЛОБАЛЬНЫЕ СТАТУСНЫЕ ТЕГИ И ЦВЕТА СоОБЩЕНИЙ
37
+ # ------------------------------------------------------------------------------
38
+ INFO = "@info"
39
+ INFO_COLOR = Blue
40
+ WARNING = "@warn"
41
+ WARNING_COLOR = Orange
42
+ ERROR = "@error"
43
+ ERROR_COLOR = Red
44
+
45
+ # 🚨 КРИТИЧЕСКИЕ ОШИБКИ И ПЕРЕХВАТЧИКИ PROCESS
46
+ # ------------------------------------------------------------------------------
47
+ CUSTOM_ERROR_MESSAGE = "Error!"
48
+ CUSTOM_ERROR_MESSAGE_COLOR = Orange
49
+ ERROR_STACK_COLOR = Red # Название переменной для safeChalk в Error
50
+
51
+ # 📦 НАСТРОЙКИ ДЛЯ ЛОГИРОВАНИЯ ВНУТРЕННИХ ФУНКЦИЙ
52
+ # ------------------------------------------------------------------------------
53
+ FUNCTION_INFO = "@function-info"
54
+ FUNCTION_INFO_COLOR = Blue
55
+ NAME_FUNCTION_COLOR = Lime # Цвет скобок функции [-authCheck-]
56
+ CUSTOM_TEXT_TO_FUNCTION_COLOR = Gray # Цвет описания работы функции
57
+ LOG = "@function-log"
58
+ LOG_COLOR = Blue
59
+ STATUS = "@function-status"
60
+ STATUS_COLOR = Blue
61
+
62
+ # 📈 МЕТРИКИ ПРОИЗВОДИТЕЛЬНОСТИ (PERFORMANCE)
63
+ # ------------------------------------------------------------------------------
64
+ POSITIVE = "@function-positive-performance"
65
+ POSITIVE_COLOR = Green
66
+ PNAME_COLOR = Lime
67
+ NEGATIVE = "@function-negative-performance"
68
+ NEGATIVE_COLOR = Orange
69
+ NNAME_COLOR = Lime
70
+
71
+ # ✉️ СЕРВИС ОТПРАВКИ ПОЧТЫ (NODEMAILER)
72
+ # ------------------------------------------------------------------------------
73
+ POSITIVE_SEND = "@send-status-true"
74
+ POSITIVE_SEND_COLOR = Green
75
+ NEGATIVE_SEND = "@send-status-false"
76
+ NEGATIVE_SEND_COLOR = Orange
77
+ GMAIL_COLOR = Lime
78
+
79
+ # 🏗️ ИЕРАРХИЯ ПРЕФИКСОВ (КОМПОНЕНТЫ И МОДУЛИ)
80
+ # ------------------------------------------------------------------------------
81
+ PREFIX_TEXT = "SYSTEM"
82
+ PREFIX_TEXT_COLOR = Blue
83
+
84
+ SERVER_TEXT = "SERVER"
85
+ SERVER_TEXT_COLOR = Blue
86
+ SOCKET_TEXT = "SOCKET"
87
+ SOCKET_TEXT_COLOR = Blue
88
+ WRITTER_TEXT = "WRITTER"
89
+ WRITTER_TEXT_COLOR = Blue
90
+ DATABASE_TEXT = "DATABASE"
91
+ DATABASE_TEXT_COLOR = Green
92
+ NODEMAILER_TEXT = "NODEMAILER"
93
+ NODEMAILER_TEXT_COLOR = Purple
94
+
95
+ MODULES_TEXT = "NODE-MODULES"
96
+ MODULES_TEXT_COLOR = Lime
97
+ MODULE_TEXT = "[ LOGGER ]"
98
+ MODULE_TEXT_COLOR = SkyBlue
99
+
100
+ # 🎨 НАСТРОЙКА ДЛЯ ЛЮБЫХ КАСТОМНЫХ ПРЕФИКСОВ (Фича v3.0.0)
101
+ # ------------------------------------------------------------------------------
102
+ CUSTOM_PREFIX_TEXT_COLOR = Green # Цвет для всех динамических префиксов (Redis, Telegram и т.д.)
103
+ CUSTOM_PREFIX_TEXT_CASE = Upper # Регистр: Upper (по умолчанию), Lower или AsIs (как в коде)
104
+
105
+ # 🚀 СТАРТОВЫЕ И ФИНАЛЬНЫЕ СООБЩЕНИЯ МОДУЛЕЙ
106
+ # ------------------------------------------------------------------------------
107
+ CUSTOM_MODULE_START_MESSAGE = "Code is ran!"
108
+ CUSTOM_MODULES_START_MESSAGE = "Module is ran!"
109
+ CUSTOM_MODULES_START_MESSAGE_IF_ISNT_MODULES = "No active neighbor modules found."
110
+ CUSTOM_MODULES_START_MESSAGE_COLOR = Gray
111
+ CUSTOM_MODULE_STOP_MESSAGE = "Code is stop!"
112
+ CUSTOM_MODULES_STOP_MESSAGE = "Module is stoped!"
113
+ CUSTOM_MODULES_STOP_MESSAGE_IF_ISNT_MODULES = "No active neighbor modules found."
114
+ CUSTOM_MODULES_STOP_MESSAGE_COLOR = Gray
Binary file
@@ -0,0 +1,33 @@
1
+ const { Sequelize } = require('sequelize');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ const dbDir = path.resolve(__dirname, '../');
6
+ const dbPath = path.join(dbDir, 'citybank.db');
7
+
8
+ if (!fs.existsSync(dbDir)) {
9
+ fs.mkdirSync(dbDir, { recursive: true });
10
+ }
11
+
12
+ try {
13
+ if (fs.existsSync(dbPath)) {
14
+ const stats = fs.statSync(dbPath);
15
+ if (stats.size === 0) {
16
+ console.warn("⚠️ [City-bank] База данных пуста или повреждена.");
17
+ }
18
+ }
19
+ } catch (e) {
20
+ console.error("❌ [-101] Файл бд не найден или повреждён");
21
+ process.exit(1);
22
+ }
23
+
24
+ const sequelize = new Sequelize({
25
+ dialect: 'sqlite',
26
+ storage: dbPath,
27
+ logging: false,
28
+ define: {
29
+ timestamps: true
30
+ }
31
+ });
32
+
33
+ module.exports = sequelize;
@@ -0,0 +1,44 @@
1
+ const { DataTypes } = require('sequelize');
2
+ const sequelize = require('../config/init-db');
3
+ const User = require('./User');
4
+
5
+ const Account = sequelize.define('account', {
6
+ id: {
7
+ type: DataTypes.INTEGER,
8
+ autoIncrement: true,
9
+ primaryKey: true
10
+ },
11
+ user: {
12
+ type: DataTypes.STRING,
13
+ allowNull: false,
14
+ references: { model: User, key: 'name' },
15
+ onDelete: 'CASCADE',
16
+ onUpdate: 'CASCADE'
17
+ },
18
+ pin_code: {
19
+ type: DataTypes.STRING,
20
+ allowNull: false,
21
+ field: 'pin-code'
22
+ },
23
+ active: {
24
+ type: DataTypes.BOOLEAN,
25
+ allowNull: false,
26
+ defaultValue: true
27
+ },
28
+ manny: {
29
+ type: DataTypes.DECIMAL(15, 2),
30
+ allowNull: false,
31
+ defaultValue: 0.00
32
+ },
33
+ two_fa_status: {
34
+ type: DataTypes.BOOLEAN,
35
+ allowNull: false,
36
+ defaultValue: false,
37
+ field: '2FA-status'
38
+ }
39
+ });
40
+
41
+ User.hasMany(Account, { foreignKey: 'user', sourceKey: 'name' });
42
+ Account.belongsTo(User, { foreignKey: 'user', targetKey: 'name', as: 'owner' });
43
+
44
+ module.exports = Account;
@@ -0,0 +1,39 @@
1
+ const { DataTypes } = require('sequelize');
2
+ const sequelize = require('../config/init-db');
3
+ const Account = require('./Account');
4
+
5
+ const Credit = sequelize.define('credit', {
6
+ id: {
7
+ type: DataTypes.INTEGER,
8
+ autoIncrement: true,
9
+ primaryKey: true
10
+ },
11
+ account_id: {
12
+ type: DataTypes.INTEGER,
13
+ allowNull: false,
14
+ references: { model: Account, key: 'id' },
15
+ onDelete: 'CASCADE'
16
+ },
17
+ total_payments: {
18
+ type: DataTypes.INTEGER,
19
+ allowNull: false
20
+ },
21
+ remaining_payments: {
22
+ type: DataTypes.INTEGER,
23
+ allowNull: false
24
+ },
25
+ payment_amount: {
26
+ type: DataTypes.DECIMAL(15, 2),
27
+ allowNull: false
28
+ },
29
+ auto_withdrawal: {
30
+ type: DataTypes.BOOLEAN,
31
+ allowNull: false,
32
+ defaultValue: false
33
+ }
34
+ });
35
+
36
+ Account.hasMany(Credit, { foreignKey: 'account_id' });
37
+ Credit.belongsTo(Account, { foreignKey: 'account_id', as: 'linkedAccount' });
38
+
39
+ module.exports = Credit;
@@ -0,0 +1,27 @@
1
+ // database/models/Transaction.js
2
+ const { DataTypes } = require('sequelize');
3
+ const sequelize = require('../config/init-db');
4
+
5
+ module.exports = sequelize.define('transaction_log', {
6
+ id: {
7
+ type: DataTypes.INTEGER,
8
+ autoIncrement: true,
9
+ primaryKey: true
10
+ },
11
+ account_id: {
12
+ type: DataTypes.INTEGER,
13
+ allowNull: false
14
+ },
15
+ type: {
16
+ type: DataTypes.ENUM('DEPOSIT', 'WITHDRAW', 'TRANSFER_OUT', 'TRANSFER_IN'),
17
+ allowNull: false
18
+ },
19
+ sender_or_receiver: {
20
+ type: DataTypes.STRING, // Никнейм контрагента (для переводов) или описание
21
+ allowNull: true
22
+ },
23
+ amount: {
24
+ type: DataTypes.DECIMAL(15, 2),
25
+ allowNull: false
26
+ }
27
+ });
@@ -0,0 +1,50 @@
1
+ const { DataTypes } = require('sequelize');
2
+ const sequelize = require('../config/init-db');
3
+
4
+ const User = sequelize.define('user', {
5
+ name: {
6
+ type: DataTypes.STRING,
7
+ primaryKey: true,
8
+ allowNull: false
9
+ },
10
+ user_name: {
11
+ type: DataTypes.STRING, // Настоящее имя (bcrypt)
12
+ allowNull: false
13
+ },
14
+ user_surname: {
15
+ type: DataTypes.STRING, // Настоящая фамилия (bcrypt)
16
+ allowNull: false
17
+ },
18
+ user_gmail: {
19
+ type: DataTypes.STRING,
20
+ allowNull: false,
21
+ unique: true
22
+ },
23
+ two_fa_status: {
24
+ type: DataTypes.BOOLEAN,
25
+ allowNull: false,
26
+ defaultValue: false,
27
+ field: '2FA-status'
28
+ },
29
+ two_fa_token: {
30
+ type: DataTypes.STRING, // Хешированный секрет аутентификатора
31
+ allowNull: true,
32
+ field: '2FA-token'
33
+ },
34
+ token: {
35
+ type: DataTypes.STRING, // Хешированный токен аварийного входа
36
+ allowNull: false
37
+ },
38
+ password: {
39
+ type: DataTypes.STRING,
40
+ allowNull: false
41
+ },
42
+ avatar: {
43
+ type: DataTypes.STRING,
44
+ allowNull: true,
45
+ defaultValue: '/uploads/avatars/default.png' // Стандартная аватарка для всех
46
+ }
47
+
48
+ });
49
+
50
+ module.exports = User;
@@ -0,0 +1,19 @@
1
+ const Account = require('../../models/Account');
2
+
3
+ module.exports = async function addManny(accountId, amount) {
4
+ try {
5
+ const acc = await Account.findByPk(accountId);
6
+ if (!acc) return { status: false, statusCode: -2, message: 'Счет не найден' };
7
+
8
+ const numericAmount = parseFloat(amount);
9
+ if (isNaN(numericAmount) || numericAmount <= 0) return { status: false, statusCode: -2 };
10
+
11
+ await acc.increment('manny', { by: numericAmount });
12
+ const Transaction = require('../../models/Transaction');
13
+ await Transaction.create({ account_id: accountId, type: 'DEPOSIT', sender_or_receiver: 'Администрация (ROOT)', amount: numericAmount });
14
+
15
+ return { status: true, statusCode: 1 };
16
+ } catch (err) {
17
+ return { status: false, statusCode: -10 };
18
+ }
19
+ };
@@ -0,0 +1,23 @@
1
+ const bcrypt = require('bcrypt');
2
+ const Account = require('../../models/Account');
3
+
4
+ module.exports = async function changePincode(accountId, oldPin, newPin) {
5
+ try {
6
+ const acc = await Account.findByPk(accountId);
7
+ if (!acc) return { status: false, statusCode: -2, message: 'Счет не найден' };
8
+
9
+ const isPinValid = await bcrypt.compare(oldPin, acc.pin_code);
10
+ if (!isPinValid) return { status: false, statusCode: -2, message: 'Неверный текущий пин-код' };
11
+
12
+ if (!newPin || newPin.length > 8) {
13
+ return { status: false, statusCode: -2, message: 'Новый пин-код должен быть до 8 символов' };
14
+ }
15
+
16
+ acc.pin_code = await bcrypt.hash(newPin, 10);
17
+ await acc.save();
18
+
19
+ return { status: true, statusCode: 1 };
20
+ } catch (err) {
21
+ return { status: false, statusCode: -10 };
22
+ }
23
+ };
@@ -0,0 +1,19 @@
1
+ const Account = require('../../models/Account');
2
+ const { sendBotNotification } = require('../bot/bot');
3
+ module.exports = async function changeAccountStatus(accountId, isActive) {
4
+ try {
5
+ const [updatedCount] = await Account.update(
6
+ { active: isActive },
7
+ { where: { id: accountId } }
8
+ );
9
+ if (updatedCount === 0) {
10
+ return { status: false, statusCode: -2, message: 'Счет не найден в системе' };
11
+ }
12
+ const templateName = isActive ? 'unban-bot' : 'ban-bot';
13
+ await sendBotNotification(templateName, { accountId });
14
+ return { status: true, statusCode: 1 };
15
+ } catch (err) {
16
+ console.error("Ошибка при изменении статуса счета:", err.message);
17
+ return { status: false, statusCode: -10, message: 'Ошибка базы данных' };
18
+ }
19
+ };
@@ -0,0 +1,106 @@
1
+ const bcrypt = require('bcrypt');
2
+ const Account = require('../../models/Account');
3
+ const Credit = require('../../models/Credit');
4
+ const User = require('../../models/User');
5
+ const nodemailer = require('nodemailer');
6
+
7
+ module.exports = async function creatCredit(username, targetAccountId, amount, pinCode, paymentsCount, autoWithdrawal) {
8
+ try {
9
+ const targetAcc = await Account.findByPk(targetAccountId);
10
+ if (!targetAcc || targetAcc.user !== username) {
11
+ return { status: false, statusCode: -2, message: 'Целевой счет не найден' };
12
+ }
13
+
14
+ // Проверка пин-кода (-23)
15
+ const isPinValid = await bcrypt.compare(pinCode, targetAcc.pin_code);
16
+ if (!isPinValid) return { status: false, statusCode: -23, message: 'Неверный пин-код счета' };
17
+
18
+ const creditAmount = parseFloat(amount);
19
+ if (isNaN(creditAmount) || creditAmount <= 0) return { status: false, statusCode: -2 };
20
+
21
+ const paymentAmount = parseFloat((creditAmount / paymentsCount).toFixed(2));
22
+
23
+ // 1. Создаем технический счет кредита, куда начисляются заемные деньги
24
+ const creditAccount = await Account.create({
25
+ user: username,
26
+ pin_code: targetAcc.pin_code,
27
+ manny: creditAmount,
28
+ active: true,
29
+ two_fa_status: false
30
+ });
31
+
32
+ // 2. Регистрируем кредит в таблице мониторинга
33
+ const newCredit = await Credit.create({
34
+ account_id: creditAccount.id,
35
+ total_payments: paymentsCount,
36
+ remaining_payments: paymentsCount,
37
+ payment_amount: paymentAmount,
38
+ auto_withdrawal: autoWithdrawal
39
+ });
40
+
41
+ /**
42
+ * Автономный метод обработки биллинга (вызывать раз в период через крон / интервал)
43
+ */
44
+ async function processCreditBilling(creditId) {
45
+ try {
46
+ const credit = await Credit.findByPk(creditId, { include: [{ model: Account, as: 'linkedAccount' }] });
47
+ if (!credit) return;
48
+
49
+ const linkedAcc = credit.linkedAccount;
50
+ const owner = await User.findByPk(username);
51
+
52
+ let paymentSuccess = false;
53
+
54
+ // Если автономное списание разрешено ТЗ
55
+ if (credit.auto_withdrawal && linkedAcc) {
56
+ if (parseFloat(linkedAcc.manny) >= parseFloat(credit.payment_amount)) {
57
+ await linkedAcc.decrement('manny', { by: credit.payment_amount });
58
+ credit.remaining_payments -= 1;
59
+ await credit.save();
60
+ paymentSuccess = true;
61
+ } else {
62
+ // Если денег не хватило, переводим в ручной режим для отправки предупреждений
63
+ credit.auto_withdrawal = false;
64
+ await credit.save();
65
+ }
66
+ }
67
+
68
+ // Если автосписание выключено или не удалось — отправляем предупреждение на почту по ТЗ
69
+ if (!paymentSuccess && owner) {
70
+ const transporter = nodemailer.createTransport({
71
+ service: 'gmail',
72
+ auth: { user: process.env.GMAIL, pass: process.env.GOOGLE_APP_PASSWORD }
73
+ });
74
+
75
+ await transporter.sendMail({
76
+ from: `"City-bank Биллинг" <${process.env.GMAIL}>`,
77
+ to: owner.user_gmail,
78
+ subject: '⚠️ Требуется оплата по кредиту | City-bank',
79
+ text: `Уважаемый клиент! По вашему кредитному счету #${credit.account_id} зафиксирован обязательный платеж. Вам необходимо внести ${credit.payment_amount} руб.`
80
+ });
81
+ }
82
+
83
+ // Если все платежи закрыты — полностью удаляем и закрываем кредит и технический счет по ТЗ
84
+ if (credit.remaining_payments <= 0) {
85
+ if (linkedAcc) await linkedAcc.destroy();
86
+ await credit.destroy();
87
+ console.log(`🎉 Кредит #${creditId} полностью погашен и закрыт.`);
88
+ }
89
+ } catch (billingErr) {
90
+ console.error("Ошибка процессинга кредита:", billingErr.message);
91
+ }
92
+ }
93
+
94
+ // Сохраняем ссылку на обработчик в глобальный крон или интервал (в рамках игровой сессии)
95
+ // Для симуляции можно вызывать функцию через setInterval()
96
+
97
+ return {
98
+ status: true,
99
+ statusCode: 1,
100
+ creditAccountId: creditAccount.id,
101
+ password: pinCode
102
+ };
103
+ } catch (err) {
104
+ return { status: false, statusCode: -10, message: err.message };
105
+ }
106
+ };
@@ -0,0 +1,24 @@
1
+ const bcrypt = require('bcrypt');
2
+ const Account = require('../../models/Account');
3
+
4
+ module.exports = async function createAccount(username, pinCode, enable2FA) {
5
+ try {
6
+ // Валидация длины пин-кода (до 8 символов по ТЗ)
7
+ if (!pinCode || pinCode.length > 8) {
8
+ return { status: false, statusCode: -2, message: 'Пин-код должен быть до 8 символов' };
9
+ }
10
+
11
+ const hashedPin = await bcrypt.hash(pinCode, 10);
12
+ const newAccount = await Account.create({
13
+ user: username,
14
+ pin_code: hashedPin,
15
+ manny: 0.00, // Строго нулевой баланс для игроков
16
+ active: true,
17
+ two_fa_status: enable2FA
18
+ });
19
+
20
+ return { status: true, statusCode: 1, accountId: newAccount.id };
21
+ } catch (err) {
22
+ return { status: false, statusCode: -10, message: err.message };
23
+ }
24
+ };
@@ -0,0 +1,23 @@
1
+ const Account = require('../../models/Account');
2
+
3
+ module.exports = async function deleteAccount(accountId, username) {
4
+ try {
5
+ const acc = await Account.findByPk(accountId);
6
+ if (!acc) return { status: false, statusCode: -2, message: 'Счет не найден' };
7
+
8
+ // Проверка ограничений ТЗ: счет должен быть активен и пуст
9
+ if (!acc.active || parseFloat(acc.manny) !== 0) {
10
+ return { status: false, statusCode: -11, message: 'Нельзя удалить заблокированный или непустой счет' };
11
+ }
12
+
13
+ // Проверка прав владения (пользователь или суперадмин root)
14
+ if (acc.user !== username && username !== 'root') {
15
+ return { status: false, statusCode: -2, message: 'Отказано в доступе' };
16
+ }
17
+
18
+ await acc.destroy();
19
+ return { status: true, statusCode: 1 };
20
+ } catch (err) {
21
+ return { status: false, statusCode: -10 };
22
+ }
23
+ };
@@ -0,0 +1,12 @@
1
+ const Account = require('../../models/Account');
2
+
3
+ module.exports = async function findManny(accountId) {
4
+ try {
5
+ const acc = await Account.findByPk(accountId);
6
+ if (!acc) return { status: false, statusCode: -2, message: 'Счет не найден' };
7
+
8
+ return { status: true, statusCode: 1, manny: parseFloat(acc.manny) };
9
+ } catch (err) {
10
+ return { status: false, statusCode: -10 };
11
+ }
12
+ };
@@ -0,0 +1,26 @@
1
+ // database/services/account/logs.js
2
+ const Account = require('../../models/Account');
3
+ const Transaction = require('../../models/Transaction');
4
+
5
+ module.exports = async function getAccountLogs(username) {
6
+ try {
7
+ // Находим все ID счетов, принадлежащих этому пользователю
8
+ const myAccounts = await Account.findAll({
9
+ where: { user: username },
10
+ attributes: ['id']
11
+ });
12
+
13
+ const accountIds = myAccounts.map(acc => acc.id);
14
+
15
+ // Запрашиваем логи по этим счетам
16
+ const logs = await Transaction.findAll({
17
+ where: { account_id: accountIds },
18
+ order: [['createdAt', 'DESC']], // Сначала самые свежие
19
+ limit: 50 // Ограничим вывод последними 50 операциями
20
+ });
21
+
22
+ return { status: true, statusCode: 1, logs };
23
+ } catch (err) {
24
+ return { status: false, statusCode: -10, message: err.message };
25
+ }
26
+ };
@@ -0,0 +1,18 @@
1
+ const Account = require('../../models/Account');
2
+
3
+ module.exports = async function removeManny(accountId, amount) {
4
+ try {
5
+ const acc = await Account.findByPk(accountId);
6
+ if (!acc) return { status: false, statusCode: -2, message: 'Счет не найден' };
7
+
8
+ const numericAmount = parseFloat(amount);
9
+ if (isNaN(numericAmount) || numericAmount <= 0) return { status: false, statusCode: -2 };
10
+
11
+ await acc.decrement('manny', { by: numericAmount });
12
+ const Transaction = require('../../models/Transaction');
13
+ await Transaction.create({ account_id: accountId, type: 'WITHDRAW', sender_or_receiver: 'Администрация (ROOT)', amount: numericAmount });
14
+ return { status: true, statusCode: 1 };
15
+ } catch (err) {
16
+ return { status: false, statusCode: -10 };
17
+ }
18
+ };