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.
- package/.env +114 -0
- package/database/citybank.db +0 -0
- package/database/config/init-db.js +33 -0
- package/database/models/Account.js +44 -0
- package/database/models/Credit.js +39 -0
- package/database/models/Transaction.js +27 -0
- package/database/models/User.js +50 -0
- package/database/services/account/add-manny.js +19 -0
- package/database/services/account/change-pincode.js +23 -0
- package/database/services/account/change-status.js +19 -0
- package/database/services/account/create-credit.js +106 -0
- package/database/services/account/create.js +24 -0
- package/database/services/account/delete.js +23 -0
- package/database/services/account/find-manny.js +12 -0
- package/database/services/account/logs.js +26 -0
- package/database/services/account/remove-manny.js +18 -0
- package/database/services/account/transfer.js +83 -0
- package/database/services/accounts.js +13 -0
- package/database/services/bot/bot.js +38 -0
- package/database/services/bot/generators/ban-bot.html +1 -0
- package/database/services/bot/generators/ban-bot.txt +1 -0
- package/database/services/bot/generators/disable-2FA-bot.html +1 -0
- package/database/services/bot/generators/disable-2FA-bot.txt +1 -0
- package/database/services/bot/generators/enable-2FA-bot.html +1 -0
- package/database/services/bot/generators/enable-2FA-bot.txt +1 -0
- package/database/services/bot/generators/login-bot.html +1 -0
- package/database/services/bot/generators/login-bot.txt +1 -0
- package/database/services/bot/generators/register-bot.html +1 -0
- package/database/services/bot/generators/register-bot.txt +7 -0
- package/database/services/bot/generators/transfer-from-bot.html +1 -0
- package/database/services/bot/generators/transfer-from-bot.txt +1 -0
- package/database/services/bot/generators/transfer-to-bot.html +1 -0
- package/database/services/bot/generators/transfer-to-bot.txt +1 -0
- package/database/services/bot/generators/unban-bot.html +1 -0
- package/database/services/bot/generators/unban-bot.txt +1 -0
- package/database/services/bot/write.js +49 -0
- package/database/services/user/change-avatar.js +16 -0
- package/database/services/user/change-gmail.js +13 -0
- package/database/services/user/change-password.js +16 -0
- package/database/services/user/create.js +56 -0
- package/database/services/user/delete.js +15 -0
- package/database/services/user/disable-2FA.js +16 -0
- package/database/services/user/enable-2FA.js +19 -0
- package/database/services/user/login.js +51 -0
- package/database/services/user/math-manny.js +8 -0
- package/database/services/users.js +11 -0
- package/index.js +83 -0
- package/logs/errors.log +14 -0
- package/package.json +29 -13
- package/public/accounts.html +44 -0
- package/public/admin.html +194 -0
- package/public/credit.html +61 -0
- package/public/css/style.css +58 -0
- package/public/general.html +53 -0
- package/public/history.html +62 -0
- package/public/home.html +378 -0
- package/public/icon/icon.png +0 -0
- package/public/js/example.html +559 -0
- package/public/js/js.js +482 -0
- package/public/js/sweetalert2.js +2 -0
- package/public/login.html +72 -0
- package/public/profile.html +78 -0
- package/public/register.html +442 -0
- package/public/transfer.html +57 -0
- package/public/uploads/avatars/Pavel_1790269874496.png +0 -0
- package/public/uploads/avatars/Pavel_1790275457821.png +0 -0
- package/public/uploads/avatars/Pavel_1790867570006.png +0 -0
- package/public/uploads/avatars/Romanenko_1790270954677.png +0 -0
- package/public/uploads/avatars/default.png +0 -0
- package/public/uploads/avatars/root_1790275537324.png +0 -0
- package/public/uploads/avatars/root_1790275537381.png +0 -0
- package/public/uploads/avatars/root_1790349234549.png +0 -0
- package/public/uploads/avatars/root_1790519734671.png +0 -0
- package/public/uploads/avatars/root_1790879784620.png +0 -0
- package/public/uploads/avatars/root_1790879790948.png +0 -0
- package/public/uploads/avatars/root_1790879796755.png +0 -0
- package/public/uploads/avatars/root_1790962800544.png +0 -0
- package/public/uploads/avatars/root_1791009911239.png +0 -0
- package/server/middleware/auth.js +34 -0
- package/server/server.js +235 -0
- 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
|
+
};
|