@emaraplay/nobles 1.0.1 → 1.1.1
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/package.json +1 -1
- package/scripts/validate-imports.js +96 -0
- package/src/games/list.method.js +25 -17
- package/src/launcher/get.method.js +7 -15
- package/src/utils/index.js +1 -0
- package/src/utils/sign.helper.js +2 -1
- package/src/utils/sortPlainObjetKeysAsc.helper.js +14 -0
- package/src/wallet/balance.method.js +2 -1
- package/src/wallet/bet-win.method.js +3 -1
- package/src/wallet/rollback.method.js +4 -2
package/package.json
CHANGED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const errors = [];
|
|
8
|
+
|
|
9
|
+
function validateFile(filePath) {
|
|
10
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
11
|
+
const dir = path.dirname(filePath);
|
|
12
|
+
|
|
13
|
+
const requireRegex = /require\(['"](\.[^'"]+)['"]\)/g;
|
|
14
|
+
let match;
|
|
15
|
+
|
|
16
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
17
|
+
const requirePath = match[1];
|
|
18
|
+
|
|
19
|
+
let fullPath = path.join(dir, requirePath);
|
|
20
|
+
|
|
21
|
+
const extensions = ['', '.js', '.json', '/index.js'];
|
|
22
|
+
let found = false;
|
|
23
|
+
let actualPath = null;
|
|
24
|
+
|
|
25
|
+
for (const ext of extensions) {
|
|
26
|
+
const testPath = fullPath + ext;
|
|
27
|
+
if (fs.existsSync(testPath)) {
|
|
28
|
+
actualPath = testPath;
|
|
29
|
+
found = true;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!found) {
|
|
35
|
+
errors.push({
|
|
36
|
+
file: filePath,
|
|
37
|
+
require: requirePath,
|
|
38
|
+
error: 'File not found'
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const expectedName = path.basename(actualPath);
|
|
44
|
+
const actualFiles = fs.readdirSync(path.dirname(actualPath));
|
|
45
|
+
|
|
46
|
+
if (!actualFiles.includes(expectedName)) {
|
|
47
|
+
const realFile = actualFiles.find(f => f.toLowerCase() === expectedName.toLowerCase());
|
|
48
|
+
|
|
49
|
+
if (realFile && realFile !== expectedName) {
|
|
50
|
+
errors.push({
|
|
51
|
+
file: filePath,
|
|
52
|
+
require: requirePath,
|
|
53
|
+
expected: expectedName,
|
|
54
|
+
actual: realFile,
|
|
55
|
+
error: 'Case mismatch (will fail on Linux)'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function scanDirectory(dir) {
|
|
63
|
+
const items = fs.readdirSync(dir);
|
|
64
|
+
|
|
65
|
+
for (const item of items) {
|
|
66
|
+
const fullPath = path.join(dir, item);
|
|
67
|
+
const stat = fs.statSync(fullPath);
|
|
68
|
+
|
|
69
|
+
if (stat.isDirectory() && item !== 'node_modules' && item !== '.git') {
|
|
70
|
+
scanDirectory(fullPath);
|
|
71
|
+
} else if (stat.isFile() && item.endsWith('.js')) {
|
|
72
|
+
validateFile(fullPath);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const srcPath = path.join(__dirname, '..', 'src');
|
|
78
|
+
console.log('🔍 Validando imports en:', srcPath);
|
|
79
|
+
scanDirectory(srcPath);
|
|
80
|
+
|
|
81
|
+
if (errors.length > 0) {
|
|
82
|
+
console.error('\n❌ Se encontraron errores de case-sensitivity:\n');
|
|
83
|
+
errors.forEach(err => {
|
|
84
|
+
console.error(`📁 ${err.file}`);
|
|
85
|
+
console.error(` require: ${err.require}`);
|
|
86
|
+
if (err.expected && err.actual) {
|
|
87
|
+
console.error(` ❌ Esperado: ${err.expected}`);
|
|
88
|
+
console.error(` ✅ Real: ${err.actual}`);
|
|
89
|
+
}
|
|
90
|
+
console.error(` Error: ${err.error}\n`);
|
|
91
|
+
});
|
|
92
|
+
process.exit(1);
|
|
93
|
+
} else {
|
|
94
|
+
console.log('✅ Todos los imports son correctos\n');
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
package/src/games/list.method.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const dayjs = require('dayjs');
|
|
4
4
|
const got = require('got');
|
|
5
|
+
const { getHMAC } = require('../utils/sign.helper');
|
|
5
6
|
|
|
6
7
|
function mapCategoryToEnum(category) {
|
|
7
8
|
if (typeof category !== 'string') return undefined;
|
|
@@ -82,13 +83,12 @@ async function getGames(provider) {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
const endpoint = provider.urls.gameList;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
86
|
+
const operator = {operatorCode: provider.params.k2};
|
|
87
|
+
const sign = getHMAC(operator, provider.params.k1);
|
|
89
88
|
const response = await got.get(endpoint, {
|
|
90
89
|
responseType: 'json',
|
|
91
90
|
headers: {
|
|
91
|
+
'x-request-sign': sign,
|
|
92
92
|
'Content-Type': 'application/json'
|
|
93
93
|
}
|
|
94
94
|
});
|
|
@@ -103,33 +103,33 @@ async function getGames(provider) {
|
|
|
103
103
|
|
|
104
104
|
for (const game of gamesData) {
|
|
105
105
|
try {
|
|
106
|
-
if (!game.
|
|
106
|
+
if (!game.code || !game.name) {
|
|
107
107
|
skippedGamesCount++;
|
|
108
108
|
continue;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const gameType = mapCategoryToEnum(game.category) || 'OTHER';
|
|
112
112
|
|
|
113
|
-
const hasFreespins = Array.isArray(game.bonusTypes) && game.bonusTypes.length > 0;
|
|
114
113
|
|
|
115
114
|
const gameData = {
|
|
116
|
-
alias: game.
|
|
117
|
-
name: game.
|
|
118
|
-
freespins:
|
|
119
|
-
fun: true,
|
|
115
|
+
alias: game.code,
|
|
116
|
+
name: game.name.trim(),
|
|
117
|
+
freespins: game.promoFeatures?.freespins || false,
|
|
118
|
+
fun: game.promoFeatures?.demo || true,
|
|
120
119
|
type: gameType,
|
|
121
120
|
platform: {
|
|
122
|
-
mobile:
|
|
121
|
+
mobile: game.mobileHorizontalStatus || false,
|
|
123
122
|
desktop: true,
|
|
124
123
|
},
|
|
124
|
+
liveGame: game.isLive || false,
|
|
125
125
|
params: {
|
|
126
|
-
|
|
126
|
+
betLevels: game.betLevels || [],
|
|
127
127
|
multiplayer: game.multiplayer || false
|
|
128
128
|
}
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
-
if (game.rtp) {
|
|
132
|
-
const rtpVal = Number(game.rtp);
|
|
131
|
+
if (game.parameters?.rtp) {
|
|
132
|
+
const rtpVal = Number(game.parameters.rtp);
|
|
133
133
|
if (!Number.isNaN(rtpVal)) {
|
|
134
134
|
const normalizedRtp = rtpVal <= 1 ? rtpVal * 100 : rtpVal;
|
|
135
135
|
if (normalizedRtp > 0 && normalizedRtp <= 100) {
|
|
@@ -137,8 +137,16 @@ async function getGames(provider) {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
if (game.releaseDate) {
|
|
141
|
+
const releaseDate = dayjs(game.releaseDate);
|
|
142
|
+
if (releaseDate.isValid()) {
|
|
143
|
+
gameData.launchDate = releaseDate.toDate();
|
|
144
|
+
} else {
|
|
145
|
+
gameData.launchDate = dayjs().toDate();
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
gameData.launchDate = dayjs().toDate();
|
|
149
|
+
}
|
|
142
150
|
|
|
143
151
|
processedGames.push(gameData);
|
|
144
152
|
} catch (gameError) {
|
|
@@ -153,7 +161,7 @@ async function getGames(provider) {
|
|
|
153
161
|
|
|
154
162
|
return processedGames;
|
|
155
163
|
} catch (error) {
|
|
156
|
-
console.error('Error obteniendo juegos de
|
|
164
|
+
console.error('Error obteniendo juegos de Nobles desde API:', error.message);
|
|
157
165
|
throw error;
|
|
158
166
|
}
|
|
159
167
|
}
|
|
@@ -4,15 +4,7 @@ const xss = require('xss');
|
|
|
4
4
|
const crypto = require('crypto');
|
|
5
5
|
const got = require('got');
|
|
6
6
|
const { getHMAC } = require('../utils/sign.helper');
|
|
7
|
-
|
|
8
|
-
function sortPlainObjectKeysAsc(obj) {
|
|
9
|
-
const sortedKeys = Object.keys(obj).sort();
|
|
10
|
-
const sortedObj = {};
|
|
11
|
-
for (const key of sortedKeys) {
|
|
12
|
-
sortedObj[key] = obj[key];
|
|
13
|
-
}
|
|
14
|
-
return sortedObj;
|
|
15
|
-
}
|
|
7
|
+
const { sortPlainObjetKeysAsc } = require('../utils');
|
|
16
8
|
|
|
17
9
|
module.exports = async function(request, provider, aUser, game, sessionId, mode) {
|
|
18
10
|
try {
|
|
@@ -20,14 +12,14 @@ module.exports = async function(request, provider, aUser, game, sessionId, mode)
|
|
|
20
12
|
if (!launchUrl) throw new Error('URL de lanzamiento (launcher) no configurada para el proveedor.');
|
|
21
13
|
const operatorId = provider?.k2;
|
|
22
14
|
if (!operatorId) throw new Error('operatorId (k2) no configurado para el proveedor.');
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
15
|
+
const gameCode = game?.a;
|
|
16
|
+
if (!gameCode) throw new Error('El ID del juego (game.a) es inválido.');
|
|
25
17
|
|
|
26
18
|
const lng = xss(request.data?.lang || aUser?.data?.language || request.body?.lang || 'es-ES');
|
|
27
19
|
const lobbyUrl = xss(request.query.lobby) || xss(request.bookmaker?.url?.l);
|
|
28
20
|
const callbackUrl = xss(provider.params?.callBack) || lobbyUrl;
|
|
29
21
|
const isDemo = mode === 'fun' || mode === 'demo';
|
|
30
|
-
const user = isDemo ?
|
|
22
|
+
const user = isDemo ? `DemoUser-${crypto.randomBytes(8).toString('hex')}` : String(aUser?.data?.internal || '');
|
|
31
23
|
const balance = isDemo ? 10000 : (aUser?.data?.balance || 0);
|
|
32
24
|
const currency = isDemo ? 'USD' : xss(aUser?.data?.currency || 'ARS');
|
|
33
25
|
let demoUrl;
|
|
@@ -37,7 +29,7 @@ module.exports = async function(request, provider, aUser, game, sessionId, mode)
|
|
|
37
29
|
demoUrl = `${provider.urls.baseUrl}/demo/sessions`;
|
|
38
30
|
const demoAuthToken = crypto.randomBytes(16).toString('hex');
|
|
39
31
|
body = {
|
|
40
|
-
gameCode
|
|
32
|
+
gameCode,
|
|
41
33
|
playerId: user,
|
|
42
34
|
operatorCode: operatorId,
|
|
43
35
|
callbackUrl: callbackUrl,
|
|
@@ -49,7 +41,7 @@ module.exports = async function(request, provider, aUser, game, sessionId, mode)
|
|
|
49
41
|
};
|
|
50
42
|
} else {
|
|
51
43
|
body = {
|
|
52
|
-
gameCode
|
|
44
|
+
gameCode,
|
|
53
45
|
playerId: user,
|
|
54
46
|
operatorCode: operatorId,
|
|
55
47
|
callbackUrl: callbackUrl,
|
|
@@ -62,7 +54,7 @@ module.exports = async function(request, provider, aUser, game, sessionId, mode)
|
|
|
62
54
|
|
|
63
55
|
}
|
|
64
56
|
|
|
65
|
-
const sortedBody =
|
|
57
|
+
const sortedBody = sortPlainObjetKeysAsc(body);
|
|
66
58
|
const signature = getHMAC(sortedBody, provider.k1);
|
|
67
59
|
|
|
68
60
|
const response = await got.post(isDemo ? demoUrl : launchUrl, {
|
package/src/utils/index.js
CHANGED
|
@@ -3,3 +3,4 @@ module.exports.getError = require('./error.helper');
|
|
|
3
3
|
module.exports.constants = require('./constants');
|
|
4
4
|
module.exports.security = require('./sign.helper');
|
|
5
5
|
module.exports.currencyHelper = require('./currency.helper');
|
|
6
|
+
module.exports.sortPlainObjetKeysAsc = require('./sortPlainObjetKeysAsc.helper');
|
package/src/utils/sign.helper.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = (obj) => {
|
|
4
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj;
|
|
5
|
+
|
|
6
|
+
const sortedKeys = Object.keys(obj).sort();
|
|
7
|
+
const sortedObj = {};
|
|
8
|
+
|
|
9
|
+
for (const key of sortedKeys) {
|
|
10
|
+
sortedObj[key] = obj[key];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return sortedObj;
|
|
14
|
+
};
|
|
@@ -6,6 +6,7 @@ const utils = require('@emaraplay/utils');
|
|
|
6
6
|
const { ERRORS, buildHttpError } = require('../utils/error.helper');
|
|
7
7
|
const { isValidSign } = require('../utils/sign.helper');
|
|
8
8
|
const { currencyHelper } = require('../utils/currency.helper');
|
|
9
|
+
const { sortPlainObjetKeysAsc } = require('../utils');
|
|
9
10
|
|
|
10
11
|
const validateAuthenticateRequest = (params) => {
|
|
11
12
|
if (!params.user) return buildHttpError(ERRORS.INVALID_USER.error);
|
|
@@ -17,7 +18,7 @@ module.exports = async function(controller, fastify, request) {
|
|
|
17
18
|
try {
|
|
18
19
|
const signature = request.headers['x-request-sign'];
|
|
19
20
|
if (!signature) return buildHttpError(ERRORS.MISSING_SIGNATURE.error);
|
|
20
|
-
const query =
|
|
21
|
+
const query = sortPlainObjetKeysAsc(request.query);
|
|
21
22
|
const secretKey = request.provider.k1;
|
|
22
23
|
if (!isValidSign(signature, query, secretKey)) {
|
|
23
24
|
return buildHttpError(ERRORS.INVALID_HASH.error);
|
|
@@ -5,6 +5,7 @@ const { ERRORS, buildHttpError } = require('../utils/error.helper');
|
|
|
5
5
|
const { normalize } = require('../utils');
|
|
6
6
|
const { isValidSign } = require('../utils/sign.helper');
|
|
7
7
|
const { currencyHelper } = require('../utils/currency.helper');
|
|
8
|
+
const { sortPlainObjetKeysAsc } = require('../utils');
|
|
8
9
|
const utils = require('@emaraplay/utils');
|
|
9
10
|
|
|
10
11
|
const validateBetRequest = (params) => {
|
|
@@ -27,7 +28,8 @@ module.exports = async function (controller, fastify, request) {
|
|
|
27
28
|
return buildHttpError(ERRORS.MISSING_SIGNATURE.error);
|
|
28
29
|
}
|
|
29
30
|
const secretKey = request.provider.k1;
|
|
30
|
-
|
|
31
|
+
const sortedBody = sortPlainObjetKeysAsc(request.body);
|
|
32
|
+
if (!isValidSign(signature, sortedBody, secretKey)) {
|
|
31
33
|
return buildHttpError(ERRORS.INVALID_HASH.error)
|
|
32
34
|
}
|
|
33
35
|
const body = normalize(request.body, request);
|
|
@@ -4,7 +4,8 @@ const Sentry = require('@sentry/node');
|
|
|
4
4
|
const { ERRORS, buildHttpError } = require('../utils/error.helper');
|
|
5
5
|
const { normalize } = require('../utils');
|
|
6
6
|
const { isValidSign } = require('../utils/sign.helper');
|
|
7
|
-
const
|
|
7
|
+
const { currencyHelper } = require('../utils/currency.helper');
|
|
8
|
+
const { sortPlainObjetKeysAsc } = require('../utils');
|
|
8
9
|
const utils = require('@emaraplay/utils');
|
|
9
10
|
|
|
10
11
|
const validateRollbackRequest = (params) => {
|
|
@@ -32,7 +33,8 @@ module.exports = async function (controller, fastify, request) {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
if (!signature) return buildHttpError(ERRORS.MISSING_SIGNATURE.error);
|
|
35
|
-
|
|
36
|
+
const sortedBody = sortPlainObjetKeysAsc(request.body);
|
|
37
|
+
if (!isValidSign(signature, sortedBody, secretKey)) {
|
|
36
38
|
return buildHttpError(ERRORS.INVALID_HASH.error);
|
|
37
39
|
}
|
|
38
40
|
|