@kaikybrofc/omnizap-system 2.2.3 → 2.2.4
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.example +8 -0
- package/README.md +29 -85
- package/app/controllers/messageController.js +133 -1
- package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +559 -136
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +19 -1
- package/app/services/lidMapService.js +4 -1
- package/app/services/whatsappLoginLinkService.js +232 -0
- package/database/migrations/20260228_0021_sticker_web_google_owner_phone.sql +83 -0
- package/package.json +1 -1
- package/public/index.html +101 -10
- package/public/js/apps/createPackApp.js +59 -272
- package/public/js/apps/homeApp.js +106 -0
- package/public/js/apps/loginApp.js +459 -0
- package/public/js/apps/stickersApp.js +34 -37
- package/public/js/apps/userApp.js +244 -0
- package/public/js/runtime/react-runtime.js +1 -0
- package/public/login/index.html +333 -0
- package/public/stickers/create/index.html +2 -1
- package/public/stickers/index.html +2 -1
- package/public/user/index.html +367 -0
- package/scripts/cache-bust.mjs +65 -11
|
@@ -8,6 +8,7 @@ import { getJidUser, normalizeJid, resolveBotJid } from '../../config/baileysCon
|
|
|
8
8
|
import { getAdminPhone, getAdminRawValue, resolveAdminJid } from '../../config/adminIdentity.js';
|
|
9
9
|
import { getActiveSocket } from '../../services/socketState.js';
|
|
10
10
|
import { extractUserIdInfo, resolveUserId } from '../../services/lidMapService.js';
|
|
11
|
+
import { resolveWhatsAppOwnerJidFromLoginPayload, toWhatsAppOwnerJid, toWhatsAppPhoneDigits } from '../../services/whatsappLoginLinkService.js';
|
|
11
12
|
import logger from '../../utils/logger/loggerModule.js';
|
|
12
13
|
import { getSystemMetrics } from '../../utils/systemMetrics/systemMetricsModule.js';
|
|
13
14
|
import {
|
|
@@ -143,12 +144,15 @@ const STICKER_API_BASE_PATH = normalizeBasePath(process.env.STICKER_API_BASE_PAT
|
|
|
143
144
|
const STICKER_ORPHAN_API_PATH = `${STICKER_API_BASE_PATH}/orphan-stickers`;
|
|
144
145
|
const STICKER_CREATE_WEB_PATH = `${STICKER_WEB_PATH}/create`;
|
|
145
146
|
const STICKER_ADMIN_WEB_PATH = `${STICKER_WEB_PATH}/admin`;
|
|
147
|
+
const STICKER_LOGIN_WEB_PATH = normalizeBasePath(process.env.STICKER_LOGIN_WEB_PATH, '/login');
|
|
148
|
+
const USER_PROFILE_WEB_PATH = normalizeBasePath(process.env.USER_PROFILE_WEB_PATH, '/user');
|
|
146
149
|
const STICKER_DATA_PUBLIC_PATH = normalizeBasePath(process.env.STICKER_DATA_PUBLIC_PATH, '/data');
|
|
147
150
|
const STICKER_DATA_PUBLIC_DIR = path.resolve(process.env.STICKER_DATA_PUBLIC_DIR || path.join(process.cwd(), 'data'));
|
|
148
151
|
const CATALOG_PUBLIC_DIR = path.resolve(process.cwd(), 'public');
|
|
149
152
|
const CATALOG_TEMPLATE_PATH = path.join(CATALOG_PUBLIC_DIR, 'stickers', 'index.html');
|
|
150
153
|
const CREATE_PACK_TEMPLATE_PATH = path.join(CATALOG_PUBLIC_DIR, 'stickers', 'create', 'index.html');
|
|
151
154
|
const ADMIN_PANEL_TEMPLATE_PATH = path.join(CATALOG_PUBLIC_DIR, 'stickers', 'admin', 'index.html');
|
|
155
|
+
const USER_DASHBOARD_TEMPLATE_PATH = path.join(CATALOG_PUBLIC_DIR, 'user', 'index.html');
|
|
152
156
|
const CATALOG_STYLES_FILE_PATH = path.join(CATALOG_PUBLIC_DIR, 'css', 'styles.css');
|
|
153
157
|
const CATALOG_SCRIPT_FILE_PATH = path.join(CATALOG_PUBLIC_DIR, 'js', 'catalog.js');
|
|
154
158
|
const DEFAULT_LIST_LIMIT = clampInt(process.env.STICKER_WEB_LIST_LIMIT, 24, 1, 60);
|
|
@@ -920,6 +924,7 @@ const normalizeGoogleWebSessionRow = (row) => {
|
|
|
920
924
|
const token = String(row.session_token || '').trim();
|
|
921
925
|
const sub = normalizeGoogleSubject(row.google_sub);
|
|
922
926
|
const ownerJid = normalizeJid(row.owner_jid) || '';
|
|
927
|
+
const ownerPhone = toWhatsAppPhoneDigits(row.owner_phone || ownerJid) || '';
|
|
923
928
|
const expiresAt = Number(new Date(row.expires_at || 0));
|
|
924
929
|
if (!token || !sub || !ownerJid || !Number.isFinite(expiresAt)) return null;
|
|
925
930
|
const createdAtRaw = Number(new Date(row.created_at || 0));
|
|
@@ -931,6 +936,7 @@ const normalizeGoogleWebSessionRow = (row) => {
|
|
|
931
936
|
name: sanitizeText(row.name || '', 120, { allowEmpty: true }) || null,
|
|
932
937
|
picture: String(row.picture_url || '').trim() || null,
|
|
933
938
|
ownerJid,
|
|
939
|
+
ownerPhone,
|
|
934
940
|
createdAt: Number.isFinite(createdAtRaw) ? createdAtRaw : Date.now(),
|
|
935
941
|
expiresAt,
|
|
936
942
|
lastSeenAt: Number.isFinite(lastSeenAtRaw) ? lastSeenAtRaw : 0,
|
|
@@ -959,10 +965,20 @@ const upsertGoogleWebUserRecord = async (user, connection = null) => {
|
|
|
959
965
|
const sub = normalizeGoogleSubject(user?.sub);
|
|
960
966
|
const ownerJid = normalizeJid(user?.ownerJid) || '';
|
|
961
967
|
if (!sub || !ownerJid) return;
|
|
968
|
+
const ownerPhone = toWhatsAppPhoneDigits(ownerJid) || null;
|
|
962
969
|
const email = String(user?.email || '').trim().toLowerCase() || null;
|
|
963
970
|
const name = sanitizeText(user?.name || '', 120, { allowEmpty: true }) || null;
|
|
964
971
|
const pictureUrl = String(user?.picture || '').trim().slice(0, 1024) || null;
|
|
965
972
|
|
|
973
|
+
// owner_jid e unico; removemos vinculos antigos desse numero antes do upsert para manter 1:1.
|
|
974
|
+
await executeQuery(
|
|
975
|
+
`DELETE FROM ${TABLES.STICKER_WEB_GOOGLE_USER}
|
|
976
|
+
WHERE owner_jid = ?
|
|
977
|
+
AND google_sub <> ?`,
|
|
978
|
+
[ownerJid, sub],
|
|
979
|
+
connection,
|
|
980
|
+
);
|
|
981
|
+
|
|
966
982
|
await executeQuery(
|
|
967
983
|
`INSERT INTO ${TABLES.STICKER_WEB_GOOGLE_USER}
|
|
968
984
|
(google_sub, owner_jid, email, name, picture_url, last_login_at, last_seen_at)
|
|
@@ -977,12 +993,22 @@ const upsertGoogleWebUserRecord = async (user, connection = null) => {
|
|
|
977
993
|
[sub, ownerJid, email, name, pictureUrl],
|
|
978
994
|
connection,
|
|
979
995
|
);
|
|
996
|
+
|
|
997
|
+
// Persistimos o telefone normalizado do owner para facilitar consultas administrativas.
|
|
998
|
+
await executeQuery(
|
|
999
|
+
`UPDATE ${TABLES.STICKER_WEB_GOOGLE_USER}
|
|
1000
|
+
SET owner_phone = COALESCE(?, owner_phone)
|
|
1001
|
+
WHERE google_sub = ?`,
|
|
1002
|
+
[ownerPhone, sub],
|
|
1003
|
+
connection,
|
|
1004
|
+
).catch(() => {});
|
|
980
1005
|
};
|
|
981
1006
|
|
|
982
1007
|
const upsertGoogleWebSessionRecord = async (session, connection = null) => {
|
|
983
1008
|
const token = String(session?.token || '').trim();
|
|
984
1009
|
const sub = normalizeGoogleSubject(session?.sub);
|
|
985
1010
|
const ownerJid = normalizeJid(session?.ownerJid) || '';
|
|
1011
|
+
const ownerPhone = toWhatsAppPhoneDigits(session?.ownerPhone || ownerJid) || null;
|
|
986
1012
|
const expiresAt = Number(session?.expiresAt || 0);
|
|
987
1013
|
if (!token || !sub || !ownerJid || !Number.isFinite(expiresAt) || expiresAt <= 0) return;
|
|
988
1014
|
const email = String(session?.email || '').trim().toLowerCase() || null;
|
|
@@ -1005,6 +1031,42 @@ const upsertGoogleWebSessionRecord = async (session, connection = null) => {
|
|
|
1005
1031
|
[token, sub, ownerJid, email, name, pictureUrl, new Date(expiresAt)],
|
|
1006
1032
|
connection,
|
|
1007
1033
|
);
|
|
1034
|
+
|
|
1035
|
+
await executeQuery(
|
|
1036
|
+
`UPDATE ${TABLES.STICKER_WEB_GOOGLE_SESSION}
|
|
1037
|
+
SET owner_phone = COALESCE(?, owner_phone)
|
|
1038
|
+
WHERE session_token = ?`,
|
|
1039
|
+
[ownerPhone, token],
|
|
1040
|
+
connection,
|
|
1041
|
+
).catch(() => {});
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
const deleteOtherGoogleWebSessionsInDb = async ({ token = '', ownerJid = '', sub = '' } = {}, connection = null) => {
|
|
1045
|
+
const sessionToken = String(token || '').trim();
|
|
1046
|
+
const normalizedOwnerJid = normalizeJid(ownerJid) || '';
|
|
1047
|
+
const normalizedSub = normalizeGoogleSubject(sub);
|
|
1048
|
+
if (!sessionToken || (!normalizedOwnerJid && !normalizedSub)) return 0;
|
|
1049
|
+
|
|
1050
|
+
const clauses = [];
|
|
1051
|
+
const params = [];
|
|
1052
|
+
if (normalizedOwnerJid) {
|
|
1053
|
+
clauses.push('owner_jid = ?');
|
|
1054
|
+
params.push(normalizedOwnerJid);
|
|
1055
|
+
}
|
|
1056
|
+
if (normalizedSub) {
|
|
1057
|
+
clauses.push('google_sub = ?');
|
|
1058
|
+
params.push(normalizedSub);
|
|
1059
|
+
}
|
|
1060
|
+
if (!clauses.length) return 0;
|
|
1061
|
+
|
|
1062
|
+
const result = await executeQuery(
|
|
1063
|
+
`DELETE FROM ${TABLES.STICKER_WEB_GOOGLE_SESSION}
|
|
1064
|
+
WHERE session_token <> ?
|
|
1065
|
+
AND (${clauses.join(' OR ')})`,
|
|
1066
|
+
[sessionToken, ...params],
|
|
1067
|
+
connection,
|
|
1068
|
+
);
|
|
1069
|
+
return Number(result?.affectedRows || 0);
|
|
1008
1070
|
};
|
|
1009
1071
|
|
|
1010
1072
|
const persistGoogleWebSessionToDb = async (session) => {
|
|
@@ -1021,6 +1083,14 @@ const persistGoogleWebSessionToDb = async (session) => {
|
|
|
1021
1083
|
},
|
|
1022
1084
|
connection,
|
|
1023
1085
|
);
|
|
1086
|
+
await deleteOtherGoogleWebSessionsInDb(
|
|
1087
|
+
{
|
|
1088
|
+
token: session.token,
|
|
1089
|
+
ownerJid: session.ownerJid,
|
|
1090
|
+
sub: session.sub,
|
|
1091
|
+
},
|
|
1092
|
+
connection,
|
|
1093
|
+
);
|
|
1024
1094
|
await upsertGoogleWebSessionRecord(session, connection);
|
|
1025
1095
|
});
|
|
1026
1096
|
};
|
|
@@ -1030,7 +1100,7 @@ const findGoogleWebSessionInDbByToken = async (sessionToken) => {
|
|
|
1030
1100
|
if (!token) return null;
|
|
1031
1101
|
await maybePruneExpiredGoogleSessionsFromDb();
|
|
1032
1102
|
const rows = await executeQuery(
|
|
1033
|
-
`SELECT session_token, google_sub, owner_jid, email, name, picture_url, created_at, expires_at, last_seen_at
|
|
1103
|
+
`SELECT session_token, google_sub, owner_jid, owner_phone, email, name, picture_url, created_at, expires_at, last_seen_at
|
|
1034
1104
|
FROM ${TABLES.STICKER_WEB_GOOGLE_SESSION}
|
|
1035
1105
|
WHERE session_token = ?
|
|
1036
1106
|
AND revoked_at IS NULL
|
|
@@ -1624,26 +1694,42 @@ const requireOwnerAdminPanelSession = (req, res) => {
|
|
|
1624
1694
|
return session;
|
|
1625
1695
|
};
|
|
1626
1696
|
|
|
1627
|
-
const createGoogleWebSession = (claims) => {
|
|
1697
|
+
const createGoogleWebSession = (claims, { ownerJid } = {}) => {
|
|
1628
1698
|
pruneExpiredGoogleSessions();
|
|
1629
1699
|
const token = randomUUID();
|
|
1630
1700
|
const now = Date.now();
|
|
1701
|
+
const resolvedOwnerJid = normalizeJid(ownerJid) || buildGoogleOwnerJid(claims.sub);
|
|
1702
|
+
const resolvedOwnerPhone = toWhatsAppPhoneDigits(resolvedOwnerJid) || '';
|
|
1631
1703
|
const session = {
|
|
1632
1704
|
token,
|
|
1633
1705
|
sub: claims.sub,
|
|
1634
1706
|
email: claims.email || null,
|
|
1635
1707
|
name: claims.name || null,
|
|
1636
1708
|
picture: claims.picture || null,
|
|
1637
|
-
ownerJid:
|
|
1709
|
+
ownerJid: resolvedOwnerJid,
|
|
1710
|
+
ownerPhone: resolvedOwnerPhone,
|
|
1638
1711
|
createdAt: now,
|
|
1639
1712
|
expiresAt: now + STICKER_WEB_GOOGLE_SESSION_TTL_MS,
|
|
1640
1713
|
lastSeenAt: now,
|
|
1641
1714
|
lastDbTouchAt: 0,
|
|
1642
1715
|
};
|
|
1643
|
-
webGoogleSessionMap.set(token, session);
|
|
1644
1716
|
return session;
|
|
1645
1717
|
};
|
|
1646
1718
|
|
|
1719
|
+
const activateGoogleWebSession = (session) => {
|
|
1720
|
+
if (!session?.token) return;
|
|
1721
|
+
pruneExpiredGoogleSessions();
|
|
1722
|
+
webGoogleSessionMap.set(session.token, session);
|
|
1723
|
+
for (const [token, existing] of webGoogleSessionMap.entries()) {
|
|
1724
|
+
if (!existing || token === session.token) continue;
|
|
1725
|
+
const sameOwner = normalizeJid(existing.ownerJid) === normalizeJid(session.ownerJid);
|
|
1726
|
+
const sameSub = normalizeGoogleSubject(existing.sub) === normalizeGoogleSubject(session.sub);
|
|
1727
|
+
if (sameOwner || sameSub) {
|
|
1728
|
+
webGoogleSessionMap.delete(token);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
|
|
1647
1733
|
const resolveGoogleWebSessionFromRequest = async (req) => {
|
|
1648
1734
|
pruneExpiredGoogleSessions();
|
|
1649
1735
|
const sessionToken = getGoogleWebSessionTokenFromRequest(req);
|
|
@@ -2266,6 +2352,27 @@ const buildSupportInfo = async () => {
|
|
|
2266
2352
|
};
|
|
2267
2353
|
};
|
|
2268
2354
|
|
|
2355
|
+
const buildBotContactInfo = () => {
|
|
2356
|
+
const phone = String(resolveCatalogBotPhone() || '').replace(/\D+/g, '');
|
|
2357
|
+
if (!phone) return null;
|
|
2358
|
+
const loginText = String(process.env.WHATSAPP_LOGIN_TRIGGER || 'iniciar').trim() || 'iniciar';
|
|
2359
|
+
const menuText = `${PACK_COMMAND_PREFIX}menu`;
|
|
2360
|
+
const buildUrl = (text) =>
|
|
2361
|
+
`https://api.whatsapp.com/send/?phone=${encodeURIComponent(phone)}&text=${encodeURIComponent(
|
|
2362
|
+
String(text || '').trim(),
|
|
2363
|
+
)}&type=custom_url&app_absent=0`;
|
|
2364
|
+
|
|
2365
|
+
return {
|
|
2366
|
+
phone,
|
|
2367
|
+
login_text: loginText,
|
|
2368
|
+
menu_text: menuText,
|
|
2369
|
+
urls: {
|
|
2370
|
+
login: buildUrl(loginText),
|
|
2371
|
+
menu: buildUrl(menuText),
|
|
2372
|
+
},
|
|
2373
|
+
};
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2269
2376
|
const listDataImageFiles = async () => {
|
|
2270
2377
|
const files = [];
|
|
2271
2378
|
const queue = [STICKER_DATA_PUBLIC_DIR];
|
|
@@ -2869,6 +2976,7 @@ const renderCatalogHtml = async ({ initialPackKey }) => {
|
|
|
2869
2976
|
__STICKER_WEB_PATH__: escapeHtmlAttribute(STICKER_WEB_PATH),
|
|
2870
2977
|
__STICKER_API_BASE_PATH__: escapeHtmlAttribute(STICKER_API_BASE_PATH),
|
|
2871
2978
|
__STICKER_ORPHAN_API_PATH__: escapeHtmlAttribute(buildOrphanStickersApiUrl()),
|
|
2979
|
+
__STICKER_LOGIN_WEB_PATH__: escapeHtmlAttribute(STICKER_LOGIN_WEB_PATH),
|
|
2872
2980
|
__STICKER_DATA_PUBLIC_PATH__: escapeHtmlAttribute(STICKER_DATA_PUBLIC_PATH),
|
|
2873
2981
|
__DEFAULT_LIST_LIMIT__: String(DEFAULT_LIST_LIMIT),
|
|
2874
2982
|
__DEFAULT_ORPHAN_LIST_LIMIT__: String(DEFAULT_ORPHAN_LIST_LIMIT),
|
|
@@ -3014,11 +3122,12 @@ const renderPackSeoHtml = ({ packSummary }) => {
|
|
|
3014
3122
|
data-web-path="${escapeHtmlAttribute(STICKER_WEB_PATH)}"
|
|
3015
3123
|
data-api-base-path="${escapeHtmlAttribute(STICKER_API_BASE_PATH)}"
|
|
3016
3124
|
data-orphan-api-path="${escapeHtmlAttribute(STICKER_ORPHAN_API_PATH)}"
|
|
3125
|
+
data-login-path="${escapeHtmlAttribute(STICKER_LOGIN_WEB_PATH)}"
|
|
3017
3126
|
data-default-limit="${DEFAULT_LIST_LIMIT}"
|
|
3018
3127
|
data-default-orphan-limit="${DEFAULT_ORPHAN_LIST_LIMIT}"
|
|
3019
3128
|
data-initial-pack-key="${escapeHtmlAttribute(packSummary?.pack_key || '')}"
|
|
3020
3129
|
></div>
|
|
3021
|
-
<script type="module" src="/js/apps/stickersApp.js?v=
|
|
3130
|
+
<script type="module" src="/js/apps/stickersApp.js?v=20260228-login-redirect-my-packs1"></script>
|
|
3022
3131
|
</body>
|
|
3023
3132
|
</html>`;
|
|
3024
3133
|
};
|
|
@@ -3054,6 +3163,7 @@ const renderCreatePackHtml = async () => {
|
|
|
3054
3163
|
const replacements = {
|
|
3055
3164
|
__STICKER_WEB_PATH__: escapeHtmlAttribute(STICKER_WEB_PATH),
|
|
3056
3165
|
__STICKER_CREATE_WEB_PATH__: escapeHtmlAttribute(STICKER_CREATE_WEB_PATH),
|
|
3166
|
+
__STICKER_LOGIN_WEB_PATH__: escapeHtmlAttribute(STICKER_LOGIN_WEB_PATH),
|
|
3057
3167
|
__STICKER_API_BASE_PATH__: escapeHtmlAttribute(STICKER_API_BASE_PATH),
|
|
3058
3168
|
__PACK_COMMAND_PREFIX__: escapeHtmlAttribute(PACK_COMMAND_PREFIX),
|
|
3059
3169
|
__CURRENT_YEAR__: String(new Date().getFullYear()),
|
|
@@ -3082,6 +3192,23 @@ const renderAdminPanelHtml = async () => {
|
|
|
3082
3192
|
return html;
|
|
3083
3193
|
};
|
|
3084
3194
|
|
|
3195
|
+
const renderUserDashboardHtml = async () => {
|
|
3196
|
+
const template = await fs.readFile(USER_DASHBOARD_TEMPLATE_PATH, 'utf8');
|
|
3197
|
+
const replacements = {
|
|
3198
|
+
__STICKER_WEB_PATH__: escapeHtmlAttribute(STICKER_WEB_PATH),
|
|
3199
|
+
__STICKER_LOGIN_WEB_PATH__: escapeHtmlAttribute(STICKER_LOGIN_WEB_PATH),
|
|
3200
|
+
__STICKER_API_BASE_PATH__: escapeHtmlAttribute(STICKER_API_BASE_PATH),
|
|
3201
|
+
__USER_PROFILE_WEB_PATH__: escapeHtmlAttribute(USER_PROFILE_WEB_PATH),
|
|
3202
|
+
__CURRENT_YEAR__: String(new Date().getFullYear()),
|
|
3203
|
+
};
|
|
3204
|
+
|
|
3205
|
+
let html = template;
|
|
3206
|
+
for (const [token, value] of Object.entries(replacements)) {
|
|
3207
|
+
html = html.replaceAll(token, value);
|
|
3208
|
+
}
|
|
3209
|
+
return html;
|
|
3210
|
+
};
|
|
3211
|
+
|
|
3085
3212
|
const buildSitemapXml = async () => {
|
|
3086
3213
|
if (SITEMAP_CACHE.expiresAt > Date.now() && SITEMAP_CACHE.xml) {
|
|
3087
3214
|
return SITEMAP_CACHE.xml;
|
|
@@ -3514,24 +3641,7 @@ const handleGoogleAuthSessionRequest = async (req, res) => {
|
|
|
3514
3641
|
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
3515
3642
|
const session = await resolveGoogleWebSessionFromRequest(req);
|
|
3516
3643
|
sendJson(req, res, 200, {
|
|
3517
|
-
data: session
|
|
3518
|
-
? {
|
|
3519
|
-
authenticated: true,
|
|
3520
|
-
provider: 'google',
|
|
3521
|
-
user: {
|
|
3522
|
-
sub: session.sub,
|
|
3523
|
-
email: session.email,
|
|
3524
|
-
name: session.name,
|
|
3525
|
-
picture: session.picture,
|
|
3526
|
-
},
|
|
3527
|
-
expires_at: toIsoOrNull(session.expiresAt),
|
|
3528
|
-
}
|
|
3529
|
-
: {
|
|
3530
|
-
authenticated: false,
|
|
3531
|
-
provider: 'google',
|
|
3532
|
-
user: null,
|
|
3533
|
-
expires_at: null,
|
|
3534
|
-
},
|
|
3644
|
+
data: mapGoogleSessionResponseData(session),
|
|
3535
3645
|
});
|
|
3536
3646
|
return;
|
|
3537
3647
|
}
|
|
@@ -3567,20 +3677,57 @@ const handleGoogleAuthSessionRequest = async (req, res) => {
|
|
|
3567
3677
|
|
|
3568
3678
|
try {
|
|
3569
3679
|
const claims = await verifyGoogleIdToken(payload?.google_id_token || payload?.id_token);
|
|
3680
|
+
const linkedOwner = resolveWhatsAppOwnerJidFromLoginPayload(payload);
|
|
3681
|
+
if (!linkedOwner.ownerJid) {
|
|
3682
|
+
if (!linkedOwner.hasPayload) {
|
|
3683
|
+
sendJson(req, res, 400, {
|
|
3684
|
+
error: 'Abra esta pagina pelo link enviado no WhatsApp. Envie "iniciar" no bot para gerar o link de login.',
|
|
3685
|
+
code: 'WHATSAPP_LOGIN_LINK_REQUIRED',
|
|
3686
|
+
reason: 'missing_link',
|
|
3687
|
+
});
|
|
3688
|
+
return;
|
|
3689
|
+
}
|
|
3690
|
+
|
|
3691
|
+
const reason = String(linkedOwner.reason || '').trim().toLowerCase();
|
|
3692
|
+
const isUnauthorizedAttempt = ['invalid_signature', 'missing_signature'].includes(reason);
|
|
3693
|
+
const statusCode = isUnauthorizedAttempt ? 403 : 400;
|
|
3694
|
+
const errorMessage =
|
|
3695
|
+
reason === 'expired'
|
|
3696
|
+
? 'Link de login expirado. Envie "iniciar" novamente no WhatsApp.'
|
|
3697
|
+
: isUnauthorizedAttempt
|
|
3698
|
+
? 'Tentativa de login sem permissao detectada. Gere um novo link enviando "iniciar" no privado do bot.'
|
|
3699
|
+
: 'Link de login invalido. Envie "iniciar" novamente no WhatsApp.';
|
|
3700
|
+
|
|
3701
|
+
logger.warn('Tentativa de login web bloqueada por validacao do link WhatsApp.', {
|
|
3702
|
+
action: 'sticker_pack_google_web_login_link_blocked',
|
|
3703
|
+
reason: reason || 'unknown',
|
|
3704
|
+
remote_ip: req.socket?.remoteAddress || null,
|
|
3705
|
+
user_agent: req.headers?.['user-agent'] || null,
|
|
3706
|
+
});
|
|
3707
|
+
|
|
3708
|
+
sendJson(req, res, statusCode, {
|
|
3709
|
+
error: errorMessage,
|
|
3710
|
+
code: 'WHATSAPP_LOGIN_LINK_INVALID',
|
|
3711
|
+
reason: reason || 'invalid_link',
|
|
3712
|
+
});
|
|
3713
|
+
return;
|
|
3714
|
+
}
|
|
3715
|
+
const ownerJid = linkedOwner.ownerJid;
|
|
3716
|
+
|
|
3570
3717
|
await assertGoogleIdentityNotBanned({
|
|
3571
3718
|
sub: claims.sub,
|
|
3572
3719
|
email: claims.email,
|
|
3573
|
-
ownerJid
|
|
3720
|
+
ownerJid,
|
|
3574
3721
|
});
|
|
3575
|
-
const session = createGoogleWebSession(claims);
|
|
3722
|
+
const session = createGoogleWebSession(claims, { ownerJid });
|
|
3576
3723
|
if (!session.ownerJid) {
|
|
3577
3724
|
sendJson(req, res, 400, { error: 'Nao foi possivel vincular a conta Google.' });
|
|
3578
3725
|
return;
|
|
3579
3726
|
}
|
|
3580
3727
|
try {
|
|
3581
3728
|
await persistGoogleWebSessionToDb(session);
|
|
3729
|
+
activateGoogleWebSession(session);
|
|
3582
3730
|
} catch (persistError) {
|
|
3583
|
-
webGoogleSessionMap.delete(session.token);
|
|
3584
3731
|
logger.error('Falha ao persistir sessão Google web no banco.', {
|
|
3585
3732
|
action: 'sticker_pack_google_web_session_db_persist_failed',
|
|
3586
3733
|
error: persistError?.message,
|
|
@@ -3596,17 +3743,7 @@ const handleGoogleAuthSessionRequest = async (req, res) => {
|
|
|
3596
3743
|
}),
|
|
3597
3744
|
);
|
|
3598
3745
|
sendJson(req, res, 200, {
|
|
3599
|
-
data:
|
|
3600
|
-
authenticated: true,
|
|
3601
|
-
provider: 'google',
|
|
3602
|
-
user: {
|
|
3603
|
-
sub: session.sub,
|
|
3604
|
-
email: session.email,
|
|
3605
|
-
name: session.name,
|
|
3606
|
-
picture: session.picture,
|
|
3607
|
-
},
|
|
3608
|
-
expires_at: toIsoOrNull(session.expiresAt),
|
|
3609
|
-
},
|
|
3746
|
+
data: mapGoogleSessionResponseData(session),
|
|
3610
3747
|
});
|
|
3611
3748
|
} catch (error) {
|
|
3612
3749
|
sendJson(req, res, Number(error?.statusCode || 401), {
|
|
@@ -3627,16 +3764,195 @@ const mapGoogleSessionResponseData = (session) =>
|
|
|
3627
3764
|
name: session.name,
|
|
3628
3765
|
picture: session.picture,
|
|
3629
3766
|
},
|
|
3767
|
+
owner_jid: session.ownerJid,
|
|
3768
|
+
owner_phone: toWhatsAppPhoneDigits(session.ownerPhone || session.ownerJid) || null,
|
|
3630
3769
|
expires_at: toIsoOrNull(session.expiresAt),
|
|
3631
3770
|
}
|
|
3632
3771
|
: {
|
|
3633
3772
|
authenticated: false,
|
|
3634
3773
|
provider: 'google',
|
|
3635
3774
|
user: null,
|
|
3775
|
+
owner_jid: null,
|
|
3776
|
+
owner_phone: null,
|
|
3636
3777
|
expires_at: null,
|
|
3637
3778
|
};
|
|
3638
3779
|
|
|
3639
|
-
const
|
|
3780
|
+
const buildOwnerLookupJids = (value) => {
|
|
3781
|
+
const normalized = normalizeJid(value) || '';
|
|
3782
|
+
if (!normalized || !normalized.includes('@')) return [];
|
|
3783
|
+
const lookup = new Set([normalized]);
|
|
3784
|
+
const phoneDigits = toWhatsAppPhoneDigits(normalized);
|
|
3785
|
+
if (!phoneDigits) return Array.from(lookup);
|
|
3786
|
+
lookup.add(normalizeJid(`${phoneDigits}@s.whatsapp.net`) || '');
|
|
3787
|
+
lookup.add(normalizeJid(`${phoneDigits}@c.us`) || '');
|
|
3788
|
+
lookup.add(normalizeJid(`${phoneDigits}@hosted`) || '');
|
|
3789
|
+
return Array.from(lookup).filter(Boolean);
|
|
3790
|
+
};
|
|
3791
|
+
|
|
3792
|
+
const appendMyProfileOwnerCandidate = (candidateSet, lookupSet, value) => {
|
|
3793
|
+
const normalized = normalizeJid(value) || '';
|
|
3794
|
+
if (!normalized || !normalized.includes('@')) return;
|
|
3795
|
+
|
|
3796
|
+
candidateSet.add(normalized);
|
|
3797
|
+
for (const lookupJid of buildOwnerLookupJids(normalized)) {
|
|
3798
|
+
lookupSet.add(lookupJid);
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
const phoneOwner = toWhatsAppOwnerJid(value);
|
|
3802
|
+
if (phoneOwner) {
|
|
3803
|
+
candidateSet.add(phoneOwner);
|
|
3804
|
+
for (const lookupJid of buildOwnerLookupJids(phoneOwner)) {
|
|
3805
|
+
lookupSet.add(lookupJid);
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
};
|
|
3809
|
+
|
|
3810
|
+
const buildPhoneSet = (...values) => {
|
|
3811
|
+
const set = new Set();
|
|
3812
|
+
for (const value of values) {
|
|
3813
|
+
const digits = toWhatsAppPhoneDigits(value);
|
|
3814
|
+
if (digits) set.add(digits);
|
|
3815
|
+
}
|
|
3816
|
+
return set;
|
|
3817
|
+
};
|
|
3818
|
+
|
|
3819
|
+
const resolveMyProfileOwnerCandidates = async (session) => {
|
|
3820
|
+
const candidates = new Set();
|
|
3821
|
+
const lookupByJid = new Set();
|
|
3822
|
+
const lidCandidates = new Set();
|
|
3823
|
+
const appendCandidate = (value) => appendMyProfileOwnerCandidate(candidates, lookupByJid, value);
|
|
3824
|
+
const trustedPhones = new Set();
|
|
3825
|
+
const blockedJids = new Set();
|
|
3826
|
+
const blockedPhones = new Set();
|
|
3827
|
+
|
|
3828
|
+
appendCandidate(session?.ownerJid);
|
|
3829
|
+
appendCandidate(toWhatsAppOwnerJid(session?.ownerPhone || session?.ownerJid));
|
|
3830
|
+
for (const phone of buildPhoneSet(session?.ownerPhone, session?.ownerJid)) {
|
|
3831
|
+
trustedPhones.add(phone);
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
const activeSocket = getActiveSocket();
|
|
3835
|
+
const botJid = normalizeJid(resolveBotJid(activeSocket?.user?.id || '') || '');
|
|
3836
|
+
if (botJid) {
|
|
3837
|
+
blockedJids.add(botJid);
|
|
3838
|
+
for (const phone of buildPhoneSet(botJid)) {
|
|
3839
|
+
blockedPhones.add(phone);
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
const legacyGoogleOwner = buildGoogleOwnerJid(session?.sub);
|
|
3844
|
+
if (legacyGoogleOwner) appendCandidate(legacyGoogleOwner);
|
|
3845
|
+
|
|
3846
|
+
const sessionResolved = await resolveUserId(extractUserIdInfo(session?.ownerJid || session?.ownerPhone || null)).catch(() => null);
|
|
3847
|
+
if (sessionResolved) {
|
|
3848
|
+
appendCandidate(sessionResolved);
|
|
3849
|
+
for (const phone of buildPhoneSet(sessionResolved)) {
|
|
3850
|
+
trustedPhones.add(phone);
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
const normalizedSub = normalizeGoogleSubject(session?.sub);
|
|
3855
|
+
if (normalizedSub) {
|
|
3856
|
+
try {
|
|
3857
|
+
const rows = await executeQuery(
|
|
3858
|
+
`SELECT owner_jid, owner_phone
|
|
3859
|
+
FROM ${TABLES.STICKER_WEB_GOOGLE_USER}
|
|
3860
|
+
WHERE google_sub = ?
|
|
3861
|
+
LIMIT 1`,
|
|
3862
|
+
[normalizedSub],
|
|
3863
|
+
);
|
|
3864
|
+
const row = Array.isArray(rows) ? rows[0] : null;
|
|
3865
|
+
appendCandidate(row?.owner_jid || '');
|
|
3866
|
+
appendCandidate(row?.owner_phone || '');
|
|
3867
|
+
const mappedResolved = await resolveUserId(extractUserIdInfo(row?.owner_jid || row?.owner_phone || null)).catch(() => null);
|
|
3868
|
+
if (mappedResolved) appendCandidate(mappedResolved);
|
|
3869
|
+
} catch (error) {
|
|
3870
|
+
logger.warn('Falha ao resolver owners para perfil web.', {
|
|
3871
|
+
action: 'sticker_pack_my_profile_owner_candidates_failed',
|
|
3872
|
+
google_sub: normalizedSub,
|
|
3873
|
+
error: error?.message,
|
|
3874
|
+
});
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
for (const ownerJid of Array.from(candidates)) {
|
|
3879
|
+
const identity = extractUserIdInfo(ownerJid);
|
|
3880
|
+
if (identity?.lid) lidCandidates.add(identity.lid);
|
|
3881
|
+
if (!identity?.lid && !identity?.jid) continue;
|
|
3882
|
+
const resolved = await resolveUserId(identity).catch(() => null);
|
|
3883
|
+
if (!resolved) continue;
|
|
3884
|
+
appendCandidate(resolved);
|
|
3885
|
+
const resolvedIdentity = extractUserIdInfo(resolved);
|
|
3886
|
+
if (resolvedIdentity?.lid) lidCandidates.add(resolvedIdentity.lid);
|
|
3887
|
+
}
|
|
3888
|
+
|
|
3889
|
+
const lookupValues = Array.from(lookupByJid).filter(Boolean);
|
|
3890
|
+
for (let offset = 0; offset < lookupValues.length; offset += 200) {
|
|
3891
|
+
const chunk = lookupValues.slice(offset, offset + 200);
|
|
3892
|
+
if (!chunk.length) continue;
|
|
3893
|
+
const placeholders = chunk.map(() => '?').join(', ');
|
|
3894
|
+
const rows = await executeQuery(
|
|
3895
|
+
`SELECT lid, jid
|
|
3896
|
+
FROM ${TABLES.LID_MAP}
|
|
3897
|
+
WHERE jid IN (${placeholders})
|
|
3898
|
+
ORDER BY last_seen DESC
|
|
3899
|
+
LIMIT 500`,
|
|
3900
|
+
chunk,
|
|
3901
|
+
).catch(() => []);
|
|
3902
|
+
|
|
3903
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
3904
|
+
appendCandidate(row?.jid || '');
|
|
3905
|
+
const resolvedLid = normalizeJid(row?.lid || '');
|
|
3906
|
+
if (resolvedLid) lidCandidates.add(resolvedLid);
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3910
|
+
for (const lid of lidCandidates) {
|
|
3911
|
+
const resolved = await resolveUserId(extractUserIdInfo(lid)).catch(() => null);
|
|
3912
|
+
if (resolved) {
|
|
3913
|
+
appendCandidate(resolved);
|
|
3914
|
+
appendCandidate(lid);
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
const filtered = [];
|
|
3919
|
+
for (const candidate of Array.from(candidates)) {
|
|
3920
|
+
const normalized = normalizeJid(candidate) || '';
|
|
3921
|
+
if (!normalized || !normalized.includes('@')) continue;
|
|
3922
|
+
if (blockedJids.has(normalized)) continue;
|
|
3923
|
+
|
|
3924
|
+
const directPhone = toWhatsAppPhoneDigits(normalized);
|
|
3925
|
+
if (directPhone && blockedPhones.has(directPhone)) continue;
|
|
3926
|
+
|
|
3927
|
+
const isGoogleOwner = normalized.endsWith('@google.oauth');
|
|
3928
|
+
if (trustedPhones.size === 0) {
|
|
3929
|
+
filtered.push(normalized);
|
|
3930
|
+
continue;
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
if (directPhone) {
|
|
3934
|
+
if (!trustedPhones.has(directPhone)) continue;
|
|
3935
|
+
filtered.push(normalized);
|
|
3936
|
+
continue;
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
const resolved = await resolveUserId(extractUserIdInfo(normalized)).catch(() => null);
|
|
3940
|
+
const resolvedPhone = toWhatsAppPhoneDigits(resolved || '');
|
|
3941
|
+
if (resolvedPhone) {
|
|
3942
|
+
if (!trustedPhones.has(resolvedPhone) || blockedPhones.has(resolvedPhone)) continue;
|
|
3943
|
+
filtered.push(normalized);
|
|
3944
|
+
continue;
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
if (isGoogleOwner) {
|
|
3948
|
+
filtered.push(normalized);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
|
|
3952
|
+
return Array.from(new Set(filtered));
|
|
3953
|
+
};
|
|
3954
|
+
|
|
3955
|
+
const handleMyProfileRequest = async (req, res, url = null) => {
|
|
3640
3956
|
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
3641
3957
|
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
3642
3958
|
return;
|
|
@@ -3669,7 +3985,65 @@ const handleMyProfileRequest = async (req, res) => {
|
|
|
3669
3985
|
return;
|
|
3670
3986
|
}
|
|
3671
3987
|
|
|
3672
|
-
const
|
|
3988
|
+
const ownerCandidates = await resolveMyProfileOwnerCandidates(session);
|
|
3989
|
+
if (!ownerCandidates.length) {
|
|
3990
|
+
sendJson(req, res, 200, {
|
|
3991
|
+
data: {
|
|
3992
|
+
auth: { google: authGoogle },
|
|
3993
|
+
session: mapGoogleSessionResponseData(session),
|
|
3994
|
+
owner_jid: session.ownerJid,
|
|
3995
|
+
owner_jids: [],
|
|
3996
|
+
packs: [],
|
|
3997
|
+
stats: {
|
|
3998
|
+
total: 0,
|
|
3999
|
+
published: 0,
|
|
4000
|
+
drafts: 0,
|
|
4001
|
+
private: 0,
|
|
4002
|
+
unlisted: 0,
|
|
4003
|
+
public: 0,
|
|
4004
|
+
},
|
|
4005
|
+
},
|
|
4006
|
+
});
|
|
4007
|
+
return;
|
|
4008
|
+
}
|
|
4009
|
+
|
|
4010
|
+
const ownerPacks = await Promise.all(
|
|
4011
|
+
ownerCandidates.map((ownerJid) => listStickerPacksByOwner(ownerJid, { limit: 200, offset: 0 })),
|
|
4012
|
+
);
|
|
4013
|
+
const includeAutoPacks = parseEnvBool(
|
|
4014
|
+
url?.searchParams?.get('include_auto'),
|
|
4015
|
+
parseEnvBool(process.env.STICKER_WEB_MY_PROFILE_INCLUDE_AUTO_PACKS, false),
|
|
4016
|
+
);
|
|
4017
|
+
|
|
4018
|
+
const dedupPacks = new Map();
|
|
4019
|
+
for (const packList of ownerPacks) {
|
|
4020
|
+
for (const pack of Array.isArray(packList) ? packList : []) {
|
|
4021
|
+
if (!pack?.id) continue;
|
|
4022
|
+
if (!includeAutoPacks && pack?.is_auto_pack === true) continue;
|
|
4023
|
+
const existing = dedupPacks.get(pack.id);
|
|
4024
|
+
if (!existing) {
|
|
4025
|
+
dedupPacks.set(pack.id, pack);
|
|
4026
|
+
continue;
|
|
4027
|
+
}
|
|
4028
|
+
const currentUpdatedAt = Date.parse(String(pack.updated_at || pack.created_at || ''));
|
|
4029
|
+
const existingUpdatedAt = Date.parse(String(existing.updated_at || existing.created_at || ''));
|
|
4030
|
+
if (Number.isFinite(currentUpdatedAt) && (!Number.isFinite(existingUpdatedAt) || currentUpdatedAt > existingUpdatedAt)) {
|
|
4031
|
+
dedupPacks.set(pack.id, pack);
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
|
|
4036
|
+
const packs = Array.from(dedupPacks.values())
|
|
4037
|
+
.sort((a, b) => {
|
|
4038
|
+
const aUpdatedAt = Date.parse(String(a?.updated_at || a?.created_at || ''));
|
|
4039
|
+
const bUpdatedAt = Date.parse(String(b?.updated_at || b?.created_at || ''));
|
|
4040
|
+
if (!Number.isFinite(aUpdatedAt) && !Number.isFinite(bUpdatedAt)) return 0;
|
|
4041
|
+
if (!Number.isFinite(aUpdatedAt)) return 1;
|
|
4042
|
+
if (!Number.isFinite(bUpdatedAt)) return -1;
|
|
4043
|
+
return bUpdatedAt - aUpdatedAt;
|
|
4044
|
+
})
|
|
4045
|
+
.slice(0, 300);
|
|
4046
|
+
|
|
3673
4047
|
const engagementByPackId = await listStickerPackEngagementByPackIds(packs.map((pack) => pack.id));
|
|
3674
4048
|
|
|
3675
4049
|
const mappedPacks = packs.map((pack) => {
|
|
@@ -3702,6 +4076,7 @@ const handleMyProfileRequest = async (req, res) => {
|
|
|
3702
4076
|
auth: { google: authGoogle },
|
|
3703
4077
|
session: mapGoogleSessionResponseData(session),
|
|
3704
4078
|
owner_jid: session.ownerJid,
|
|
4079
|
+
owner_jids: ownerCandidates,
|
|
3705
4080
|
packs: mappedPacks,
|
|
3706
4081
|
stats,
|
|
3707
4082
|
},
|
|
@@ -3891,23 +4266,39 @@ const loadOwnedPackForWebManagement = async (req, res, packKey, { allowMissing =
|
|
|
3891
4266
|
return null;
|
|
3892
4267
|
}
|
|
3893
4268
|
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
4269
|
+
const ownerCandidatesRaw = await resolveMyProfileOwnerCandidates(session).catch(() => []);
|
|
4270
|
+
const ownerCandidates = Array.from(new Set([normalizeJid(session.ownerJid) || '', ...ownerCandidatesRaw].filter(Boolean)));
|
|
4271
|
+
const fallbackOwnerJid = ownerCandidates[0] || normalizeJid(session.ownerJid) || '';
|
|
4272
|
+
|
|
4273
|
+
for (const ownerJid of ownerCandidates) {
|
|
4274
|
+
try {
|
|
4275
|
+
const pack = await stickerPackService.getPackInfo({
|
|
4276
|
+
ownerJid,
|
|
4277
|
+
identifier: normalizedPackKey,
|
|
4278
|
+
});
|
|
4279
|
+
return { session, ownerJid, ownerCandidates, packKey: normalizedPackKey, pack };
|
|
4280
|
+
} catch (error) {
|
|
4281
|
+
if (error instanceof StickerPackError && error.code === STICKER_PACK_ERROR_CODES.PACK_NOT_FOUND) {
|
|
4282
|
+
continue;
|
|
4283
|
+
}
|
|
4284
|
+
const mapped = mapStickerPackWebManageError(error);
|
|
4285
|
+
sendJson(req, res, mapped.statusCode, {
|
|
4286
|
+
error: mapped.message,
|
|
4287
|
+
code: mapped.code,
|
|
4288
|
+
});
|
|
4289
|
+
return null;
|
|
3903
4290
|
}
|
|
3904
|
-
const mapped = mapStickerPackWebManageError(error);
|
|
3905
|
-
sendJson(req, res, mapped.statusCode, {
|
|
3906
|
-
error: mapped.message,
|
|
3907
|
-
code: mapped.code,
|
|
3908
|
-
});
|
|
3909
|
-
return null;
|
|
3910
4291
|
}
|
|
4292
|
+
|
|
4293
|
+
if (allowMissing) {
|
|
4294
|
+
return { session, ownerJid: fallbackOwnerJid, ownerCandidates, packKey: normalizedPackKey, pack: null, missing: true };
|
|
4295
|
+
}
|
|
4296
|
+
|
|
4297
|
+
sendJson(req, res, 404, {
|
|
4298
|
+
error: 'Pack nao encontrado para este usuario.',
|
|
4299
|
+
code: STICKER_PACK_ERROR_CODES.PACK_NOT_FOUND,
|
|
4300
|
+
});
|
|
4301
|
+
return null;
|
|
3911
4302
|
};
|
|
3912
4303
|
|
|
3913
4304
|
const buildManagedPackAnalytics = async (pack) => {
|
|
@@ -3990,7 +4381,7 @@ const handleManagedPackRequest = async (req, res, packKey) => {
|
|
|
3990
4381
|
const isMutableMethod = req.method === 'PATCH' || req.method === 'DELETE';
|
|
3991
4382
|
const context = await loadOwnedPackForWebManagement(req, res, packKey, { allowMissing: isMutableMethod });
|
|
3992
4383
|
if (!context) return;
|
|
3993
|
-
const {
|
|
4384
|
+
const { packKey: normalizedPackKey } = context;
|
|
3994
4385
|
|
|
3995
4386
|
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
3996
4387
|
await sendManagedPackResponse(req, res, context.pack);
|
|
@@ -4008,7 +4399,7 @@ const handleManagedPackRequest = async (req, res, packKey) => {
|
|
|
4008
4399
|
|
|
4009
4400
|
try {
|
|
4010
4401
|
const result = await deleteManagedPackWithCleanup({
|
|
4011
|
-
ownerJid:
|
|
4402
|
+
ownerJid: context.ownerJid,
|
|
4012
4403
|
identifier: normalizedPackKey,
|
|
4013
4404
|
fallbackPack: context.pack,
|
|
4014
4405
|
});
|
|
@@ -4059,13 +4450,13 @@ const handleManagedPackRequest = async (req, res, packKey) => {
|
|
|
4059
4450
|
const currentName = sanitizeText(updatedPack?.name, PACK_CREATE_MAX_NAME_LENGTH, { allowEmpty: false });
|
|
4060
4451
|
if (!nextName) {
|
|
4061
4452
|
updatedPack = await stickerPackService.renamePack({
|
|
4062
|
-
ownerJid:
|
|
4453
|
+
ownerJid: context.ownerJid,
|
|
4063
4454
|
identifier: normalizedPackKey,
|
|
4064
4455
|
name: payload.name,
|
|
4065
4456
|
});
|
|
4066
4457
|
} else if (nextName !== currentName) {
|
|
4067
4458
|
updatedPack = await stickerPackService.renamePack({
|
|
4068
|
-
ownerJid:
|
|
4459
|
+
ownerJid: context.ownerJid,
|
|
4069
4460
|
identifier: normalizedPackKey,
|
|
4070
4461
|
name: payload.name,
|
|
4071
4462
|
});
|
|
@@ -4078,13 +4469,13 @@ const handleManagedPackRequest = async (req, res, packKey) => {
|
|
|
4078
4469
|
const currentPublisher = sanitizeText(updatedPack?.publisher, PACK_CREATE_MAX_PUBLISHER_LENGTH, { allowEmpty: false });
|
|
4079
4470
|
if (!nextPublisher) {
|
|
4080
4471
|
updatedPack = await stickerPackService.setPackPublisher({
|
|
4081
|
-
ownerJid:
|
|
4472
|
+
ownerJid: context.ownerJid,
|
|
4082
4473
|
identifier: normalizedPackKey,
|
|
4083
4474
|
publisher: payload.publisher,
|
|
4084
4475
|
});
|
|
4085
4476
|
} else if (nextPublisher !== currentPublisher) {
|
|
4086
4477
|
updatedPack = await stickerPackService.setPackPublisher({
|
|
4087
|
-
ownerJid:
|
|
4478
|
+
ownerJid: context.ownerJid,
|
|
4088
4479
|
identifier: normalizedPackKey,
|
|
4089
4480
|
publisher: payload.publisher,
|
|
4090
4481
|
});
|
|
@@ -4097,13 +4488,13 @@ const handleManagedPackRequest = async (req, res, packKey) => {
|
|
|
4097
4488
|
const currentVisibility = String(updatedPack?.visibility || '').trim().toLowerCase();
|
|
4098
4489
|
if (!nextVisibility) {
|
|
4099
4490
|
updatedPack = await stickerPackService.setPackVisibility({
|
|
4100
|
-
ownerJid:
|
|
4491
|
+
ownerJid: context.ownerJid,
|
|
4101
4492
|
identifier: normalizedPackKey,
|
|
4102
4493
|
visibility: payload.visibility,
|
|
4103
4494
|
});
|
|
4104
4495
|
} else if (nextVisibility !== currentVisibility) {
|
|
4105
4496
|
updatedPack = await stickerPackService.setPackVisibility({
|
|
4106
|
-
ownerJid:
|
|
4497
|
+
ownerJid: context.ownerJid,
|
|
4107
4498
|
identifier: normalizedPackKey,
|
|
4108
4499
|
visibility: payload.visibility,
|
|
4109
4500
|
});
|
|
@@ -4119,7 +4510,7 @@ const handleManagedPackRequest = async (req, res, packKey) => {
|
|
|
4119
4510
|
const currentDescriptionWithTags = buildPackDescriptionWithTags(currentMeta.cleanDescription || '', currentMeta.tags);
|
|
4120
4511
|
if (String(descriptionWithTags || '') !== String(currentDescriptionWithTags || '')) {
|
|
4121
4512
|
updatedPack = await stickerPackService.setPackDescription({
|
|
4122
|
-
ownerJid:
|
|
4513
|
+
ownerJid: context.ownerJid,
|
|
4123
4514
|
identifier: normalizedPackKey,
|
|
4124
4515
|
description: descriptionWithTags || '',
|
|
4125
4516
|
});
|
|
@@ -4167,7 +4558,7 @@ const handleManagedPackCloneRequest = async (req, res, packKey) => {
|
|
|
4167
4558
|
|
|
4168
4559
|
try {
|
|
4169
4560
|
const cloned = await stickerPackService.clonePack({
|
|
4170
|
-
ownerJid: context.
|
|
4561
|
+
ownerJid: context.ownerJid,
|
|
4171
4562
|
identifier: context.packKey,
|
|
4172
4563
|
newName,
|
|
4173
4564
|
});
|
|
@@ -4205,7 +4596,7 @@ const handleManagedPackCoverRequest = async (req, res, packKey) => {
|
|
|
4205
4596
|
|
|
4206
4597
|
try {
|
|
4207
4598
|
const updated = await stickerPackService.setPackCover({
|
|
4208
|
-
ownerJid: context.
|
|
4599
|
+
ownerJid: context.ownerJid,
|
|
4209
4600
|
identifier: context.packKey,
|
|
4210
4601
|
stickerId: payload?.sticker_id,
|
|
4211
4602
|
});
|
|
@@ -4218,7 +4609,7 @@ const handleManagedPackCoverRequest = async (req, res, packKey) => {
|
|
|
4218
4609
|
}
|
|
4219
4610
|
if (error instanceof StickerPackError && error.code === STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND) {
|
|
4220
4611
|
const fresh = await stickerPackService
|
|
4221
|
-
.getPackInfo({ ownerJid: context.
|
|
4612
|
+
.getPackInfo({ ownerJid: context.ownerJid, identifier: context.packKey })
|
|
4222
4613
|
.catch(() => context.pack);
|
|
4223
4614
|
await sendManagedPackMutationStatus(req, res, 'already_deleted', fresh, {
|
|
4224
4615
|
pack_key: context.packKey,
|
|
@@ -4270,7 +4661,7 @@ const handleManagedPackReorderRequest = async (req, res, packKey) => {
|
|
|
4270
4661
|
|
|
4271
4662
|
try {
|
|
4272
4663
|
const updated = await stickerPackService.reorderPackItems({
|
|
4273
|
-
ownerJid: context.
|
|
4664
|
+
ownerJid: context.ownerJid,
|
|
4274
4665
|
identifier: context.packKey,
|
|
4275
4666
|
orderStickerIds: requestedOrderIds,
|
|
4276
4667
|
});
|
|
@@ -4283,7 +4674,7 @@ const handleManagedPackReorderRequest = async (req, res, packKey) => {
|
|
|
4283
4674
|
}
|
|
4284
4675
|
if (error instanceof StickerPackError && error.code === STICKER_PACK_ERROR_CODES.INVALID_INPUT) {
|
|
4285
4676
|
const fresh = await stickerPackService
|
|
4286
|
-
.getPackInfo({ ownerJid: context.
|
|
4677
|
+
.getPackInfo({ ownerJid: context.ownerJid, identifier: context.packKey })
|
|
4287
4678
|
.catch(() => context.pack);
|
|
4288
4679
|
await sendManagedPackMutationStatus(req, res, 'noop', fresh, {
|
|
4289
4680
|
pack_key: context.packKey,
|
|
@@ -4313,7 +4704,7 @@ const handleManagedPackStickerDeleteRequest = async (req, res, packKey, stickerI
|
|
|
4313
4704
|
|
|
4314
4705
|
try {
|
|
4315
4706
|
const result = await stickerPackService.removeStickerFromPack({
|
|
4316
|
-
ownerJid: context.
|
|
4707
|
+
ownerJid: context.ownerJid,
|
|
4317
4708
|
identifier: context.packKey,
|
|
4318
4709
|
selector: stickerId,
|
|
4319
4710
|
});
|
|
@@ -4336,7 +4727,7 @@ const handleManagedPackStickerDeleteRequest = async (req, res, packKey, stickerI
|
|
|
4336
4727
|
}
|
|
4337
4728
|
if (error instanceof StickerPackError && error.code === STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND) {
|
|
4338
4729
|
const fresh = await stickerPackService
|
|
4339
|
-
.getPackInfo({ ownerJid: context.
|
|
4730
|
+
.getPackInfo({ ownerJid: context.ownerJid, identifier: context.packKey })
|
|
4340
4731
|
.catch(() => context.pack);
|
|
4341
4732
|
await sendManagedPackMutationStatus(req, res, 'already_deleted', fresh, {
|
|
4342
4733
|
pack_key: context.packKey,
|
|
@@ -4390,19 +4781,19 @@ const handleManagedPackStickerCreateRequest = async (req, res, packKey) => {
|
|
|
4390
4781
|
let uploadedAssetId = '';
|
|
4391
4782
|
try {
|
|
4392
4783
|
const normalizedUpload = await convertUploadMediaToWebp({
|
|
4393
|
-
ownerJid: context.
|
|
4784
|
+
ownerJid: context.ownerJid,
|
|
4394
4785
|
buffer: decoded.buffer,
|
|
4395
4786
|
mimetype: decoded.mimetype || 'image/webp',
|
|
4396
4787
|
});
|
|
4397
4788
|
const asset = await saveStickerAssetFromBuffer({
|
|
4398
|
-
ownerJid: context.
|
|
4789
|
+
ownerJid: context.ownerJid,
|
|
4399
4790
|
buffer: normalizedUpload.buffer,
|
|
4400
4791
|
mimetype: normalizedUpload.mimetype || 'image/webp',
|
|
4401
4792
|
});
|
|
4402
4793
|
uploadedAssetId = String(asset?.id || '').trim();
|
|
4403
4794
|
|
|
4404
4795
|
let updatedPack = await stickerPackService.addStickerToPack({
|
|
4405
|
-
ownerJid: context.
|
|
4796
|
+
ownerJid: context.ownerJid,
|
|
4406
4797
|
identifier: context.packKey,
|
|
4407
4798
|
asset: { id: uploadedAssetId },
|
|
4408
4799
|
emojis: [],
|
|
@@ -4411,7 +4802,7 @@ const handleManagedPackStickerCreateRequest = async (req, res, packKey) => {
|
|
|
4411
4802
|
|
|
4412
4803
|
if (payload?.set_cover === true) {
|
|
4413
4804
|
updatedPack = await stickerPackService.setPackCover({
|
|
4414
|
-
ownerJid: context.
|
|
4805
|
+
ownerJid: context.ownerJid,
|
|
4415
4806
|
identifier: context.packKey,
|
|
4416
4807
|
stickerId: uploadedAssetId,
|
|
4417
4808
|
});
|
|
@@ -4490,7 +4881,7 @@ const handleManagedPackStickerReplaceRequest = async (req, res, packKey, sticker
|
|
|
4490
4881
|
let uploadedAssetId = '';
|
|
4491
4882
|
try {
|
|
4492
4883
|
const originalPack = await stickerPackService.getPackInfo({
|
|
4493
|
-
ownerJid: context.
|
|
4884
|
+
ownerJid: context.ownerJid,
|
|
4494
4885
|
identifier: context.packKey,
|
|
4495
4886
|
});
|
|
4496
4887
|
const originalItems = Array.isArray(originalPack?.items) ? originalPack.items : [];
|
|
@@ -4504,12 +4895,12 @@ const handleManagedPackStickerReplaceRequest = async (req, res, packKey, sticker
|
|
|
4504
4895
|
}
|
|
4505
4896
|
|
|
4506
4897
|
const normalizedUpload = await convertUploadMediaToWebp({
|
|
4507
|
-
ownerJid: context.
|
|
4898
|
+
ownerJid: context.ownerJid,
|
|
4508
4899
|
buffer: decoded.buffer,
|
|
4509
4900
|
mimetype: decoded.mimetype || 'image/webp',
|
|
4510
4901
|
});
|
|
4511
4902
|
const asset = await saveStickerAssetFromBuffer({
|
|
4512
|
-
ownerJid: context.
|
|
4903
|
+
ownerJid: context.ownerJid,
|
|
4513
4904
|
buffer: normalizedUpload.buffer,
|
|
4514
4905
|
mimetype: normalizedUpload.mimetype || 'image/webp',
|
|
4515
4906
|
});
|
|
@@ -4525,7 +4916,7 @@ const handleManagedPackStickerReplaceRequest = async (req, res, packKey, sticker
|
|
|
4525
4916
|
}
|
|
4526
4917
|
|
|
4527
4918
|
const swapResult = await runSqlTransaction(async (connection) => {
|
|
4528
|
-
const packRow = await findStickerPackByOwnerAndIdentifier(context.
|
|
4919
|
+
const packRow = await findStickerPackByOwnerAndIdentifier(context.ownerJid, context.packKey, { connection });
|
|
4529
4920
|
if (!packRow) return { status: 'pack_missing' };
|
|
4530
4921
|
|
|
4531
4922
|
const liveOldItem = await getStickerPackItemByStickerId(packRow.id, normalizedStickerId, connection);
|
|
@@ -4577,7 +4968,7 @@ const handleManagedPackStickerReplaceRequest = async (req, res, packKey, sticker
|
|
|
4577
4968
|
|
|
4578
4969
|
if (swapResult?.status === 'old_sticker_missing') {
|
|
4579
4970
|
const fresh = await stickerPackService
|
|
4580
|
-
.getPackInfo({ ownerJid: context.
|
|
4971
|
+
.getPackInfo({ ownerJid: context.ownerJid, identifier: context.packKey })
|
|
4581
4972
|
.catch(() => originalPack);
|
|
4582
4973
|
await cleanupOrphanStickerAssets(uploadedAssetId ? [uploadedAssetId] : [], { reason: 'replace_sticker_old_missing' });
|
|
4583
4974
|
await sendManagedPackMutationStatus(req, res, 'already_deleted', fresh, {
|
|
@@ -4589,7 +4980,7 @@ const handleManagedPackStickerReplaceRequest = async (req, res, packKey, sticker
|
|
|
4589
4980
|
|
|
4590
4981
|
if (swapResult?.status === 'duplicate_target') {
|
|
4591
4982
|
const fresh = await stickerPackService
|
|
4592
|
-
.getPackInfo({ ownerJid: context.
|
|
4983
|
+
.getPackInfo({ ownerJid: context.ownerJid, identifier: context.packKey })
|
|
4593
4984
|
.catch(() => originalPack);
|
|
4594
4985
|
await sendManagedPackMutationStatus(req, res, 'noop', fresh, {
|
|
4595
4986
|
pack_key: context.packKey,
|
|
@@ -4602,7 +4993,7 @@ const handleManagedPackStickerReplaceRequest = async (req, res, packKey, sticker
|
|
|
4602
4993
|
|
|
4603
4994
|
invalidateStickerCatalogDerivedCaches();
|
|
4604
4995
|
const finalPack = await stickerPackService.getPackInfo({
|
|
4605
|
-
ownerJid: context.
|
|
4996
|
+
ownerJid: context.ownerJid,
|
|
4606
4997
|
identifier: context.packKey,
|
|
4607
4998
|
});
|
|
4608
4999
|
await cleanupOrphanStickerAssets([normalizedStickerId], { reason: 'replace_sticker_old_cleanup' });
|
|
@@ -4715,55 +5106,37 @@ const handleCreatePackRequest = async (req, res) => {
|
|
|
4715
5106
|
const manualTags = mergeUniqueTags(Array.isArray(payload?.tags) ? payload.tags : []).slice(0, 8);
|
|
4716
5107
|
const persistedDescription = buildPackDescriptionWithTags(description, manualTags);
|
|
4717
5108
|
const visibility = String(payload?.visibility || 'public').trim().toLowerCase();
|
|
4718
|
-
const explicitOwnerJid = toOwnerJid(payload?.owner_jid);
|
|
4719
5109
|
const googleSession = await resolveGoogleWebSessionFromRequest(req);
|
|
4720
|
-
|
|
5110
|
+
if (!googleSession?.ownerJid || !googleSession?.sub) {
|
|
5111
|
+
sendJson(req, res, 401, {
|
|
5112
|
+
error: 'Sessão expirada ou ausente. Faça login novamente para criar packs.',
|
|
5113
|
+
code: STICKER_PACK_ERROR_CODES.NOT_ALLOWED,
|
|
5114
|
+
});
|
|
5115
|
+
return;
|
|
5116
|
+
}
|
|
4721
5117
|
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
ownerJid: googleSession.ownerJid,
|
|
5118
|
+
try {
|
|
5119
|
+
await assertGoogleIdentityNotBanned({
|
|
4725
5120
|
sub: googleSession.sub,
|
|
4726
5121
|
email: googleSession.email,
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
const googleOwnerJid = buildGoogleOwnerJid(googleClaims.sub);
|
|
4734
|
-
await assertGoogleIdentityNotBanned({
|
|
4735
|
-
sub: googleClaims.sub,
|
|
4736
|
-
email: googleClaims.email,
|
|
4737
|
-
ownerJid: googleOwnerJid,
|
|
4738
|
-
});
|
|
4739
|
-
if (!googleOwnerJid) {
|
|
4740
|
-
sendJson(req, res, 400, {
|
|
4741
|
-
error: 'Não foi possível vincular a conta Google ao criador.',
|
|
4742
|
-
code: STICKER_PACK_ERROR_CODES.INVALID_INPUT,
|
|
4743
|
-
});
|
|
4744
|
-
return;
|
|
4745
|
-
}
|
|
4746
|
-
googleCreator = {
|
|
4747
|
-
ownerJid: googleOwnerJid,
|
|
4748
|
-
...googleClaims,
|
|
4749
|
-
};
|
|
4750
|
-
} catch (error) {
|
|
4751
|
-
sendJson(req, res, Number(error?.statusCode || 401), {
|
|
4752
|
-
error: error?.message || 'Login Google inválido.',
|
|
4753
|
-
code: STICKER_PACK_ERROR_CODES.NOT_ALLOWED,
|
|
4754
|
-
});
|
|
4755
|
-
return;
|
|
4756
|
-
}
|
|
4757
|
-
}
|
|
4758
|
-
|
|
4759
|
-
if (STICKER_WEB_GOOGLE_AUTH_REQUIRED && !googleCreator) {
|
|
4760
|
-
sendJson(req, res, 400, {
|
|
4761
|
-
error: 'Faça login com Google para criar packs nesta página.',
|
|
4762
|
-
code: STICKER_PACK_ERROR_CODES.INVALID_INPUT,
|
|
5122
|
+
ownerJid: googleSession.ownerJid,
|
|
5123
|
+
});
|
|
5124
|
+
} catch (error) {
|
|
5125
|
+
sendJson(req, res, Number(error?.statusCode || 403), {
|
|
5126
|
+
error: error?.message || 'Conta sem permissão para criar packs.',
|
|
5127
|
+
code: STICKER_PACK_ERROR_CODES.NOT_ALLOWED,
|
|
4763
5128
|
});
|
|
4764
5129
|
return;
|
|
4765
5130
|
}
|
|
4766
5131
|
|
|
5132
|
+
const googleCreator = {
|
|
5133
|
+
ownerJid: googleSession.ownerJid,
|
|
5134
|
+
sub: googleSession.sub,
|
|
5135
|
+
email: googleSession.email,
|
|
5136
|
+
name: googleSession.name,
|
|
5137
|
+
picture: googleSession.picture,
|
|
5138
|
+
};
|
|
5139
|
+
|
|
4767
5140
|
if (googleCreator?.sub && googleCreator?.ownerJid) {
|
|
4768
5141
|
await upsertGoogleWebUserRecord({
|
|
4769
5142
|
sub: googleCreator.sub,
|
|
@@ -4779,17 +5152,7 @@ const handleCreatePackRequest = async (req, res) => {
|
|
|
4779
5152
|
});
|
|
4780
5153
|
}
|
|
4781
5154
|
|
|
4782
|
-
const ownerJid = googleCreator
|
|
4783
|
-
|
|
4784
|
-
if (!ownerJid) {
|
|
4785
|
-
sendJson(req, res, 400, {
|
|
4786
|
-
error: STICKER_WEB_GOOGLE_AUTH_REQUIRED
|
|
4787
|
-
? 'Faça login com Google para criar packs nesta página.'
|
|
4788
|
-
: 'Não foi possível resolver owner_jid para criar o pack.',
|
|
4789
|
-
code: STICKER_PACK_ERROR_CODES.INVALID_INPUT,
|
|
4790
|
-
});
|
|
4791
|
-
return;
|
|
4792
|
-
}
|
|
5155
|
+
const ownerJid = googleCreator.ownerJid;
|
|
4793
5156
|
|
|
4794
5157
|
try {
|
|
4795
5158
|
logPackWebFlow('info', 'create_pack_start', {
|
|
@@ -5581,6 +5944,7 @@ const buildSystemSummarySnapshot = async () => {
|
|
|
5581
5944
|
|
|
5582
5945
|
const socketReadyState = Number(activeSocket?.ws?.readyState);
|
|
5583
5946
|
const botJid = resolveBotJid(activeSocket?.user?.id) || null;
|
|
5947
|
+
const botPhone = String(resolveCatalogBotPhone() || '').replace(/\D+/g, '') || null;
|
|
5584
5948
|
const botConnected = Boolean(botJid) && socketReadyState === 1;
|
|
5585
5949
|
const botConnectionStatus = botConnected
|
|
5586
5950
|
? 'online'
|
|
@@ -5646,6 +6010,7 @@ const buildSystemSummarySnapshot = async () => {
|
|
|
5646
6010
|
connected: botConnected,
|
|
5647
6011
|
connection_status: botConnectionStatus,
|
|
5648
6012
|
jid: botJid,
|
|
6013
|
+
phone: botPhone,
|
|
5649
6014
|
ready_state: Number.isFinite(socketReadyState) ? socketReadyState : null,
|
|
5650
6015
|
},
|
|
5651
6016
|
platform,
|
|
@@ -6510,6 +6875,15 @@ const handleSupportInfoRequest = async (req, res) => {
|
|
|
6510
6875
|
sendJson(req, res, 200, { data });
|
|
6511
6876
|
};
|
|
6512
6877
|
|
|
6878
|
+
const handleBotContactInfoRequest = async (req, res) => {
|
|
6879
|
+
const data = buildBotContactInfo();
|
|
6880
|
+
if (!data) {
|
|
6881
|
+
sendJson(req, res, 404, { error: 'Contato do bot indisponivel no momento.' });
|
|
6882
|
+
return;
|
|
6883
|
+
}
|
|
6884
|
+
sendJson(req, res, 200, { data });
|
|
6885
|
+
};
|
|
6886
|
+
|
|
6513
6887
|
const handlePublicDataAssetRequest = async (req, res, pathname) => {
|
|
6514
6888
|
const suffix = pathname.slice(STICKER_DATA_PUBLIC_PATH.length).replace(/^\/+/, '');
|
|
6515
6889
|
if (!suffix) {
|
|
@@ -6759,7 +7133,7 @@ const handlePackInteractionRequest = async (req, res, packKey, interaction, url)
|
|
|
6759
7133
|
const listAdminActiveGoogleWebSessions = async ({ limit = 200 } = {}) => {
|
|
6760
7134
|
const safeLimit = Math.max(1, Math.min(500, Number(limit || 200)));
|
|
6761
7135
|
const rows = await executeQuery(
|
|
6762
|
-
`SELECT session_token, google_sub, owner_jid, email, name, picture_url, created_at, last_seen_at, expires_at
|
|
7136
|
+
`SELECT session_token, google_sub, owner_jid, owner_phone, email, name, picture_url, created_at, last_seen_at, expires_at
|
|
6763
7137
|
FROM ${TABLES.STICKER_WEB_GOOGLE_SESSION}
|
|
6764
7138
|
WHERE revoked_at IS NULL
|
|
6765
7139
|
AND expires_at > UTC_TIMESTAMP()
|
|
@@ -6770,6 +7144,7 @@ const listAdminActiveGoogleWebSessions = async ({ limit = 200 } = {}) => {
|
|
|
6770
7144
|
session_token: String(row.session_token || '').trim(),
|
|
6771
7145
|
google_sub: normalizeGoogleSubject(row.google_sub),
|
|
6772
7146
|
owner_jid: normalizeJid(row.owner_jid) || null,
|
|
7147
|
+
owner_phone: toWhatsAppPhoneDigits(row.owner_phone || row.owner_jid) || null,
|
|
6773
7148
|
email: normalizeEmail(row.email) || null,
|
|
6774
7149
|
name: sanitizeText(row.name || '', 120, { allowEmpty: true }) || null,
|
|
6775
7150
|
picture: String(row.picture_url || '').trim() || null,
|
|
@@ -6782,7 +7157,7 @@ const listAdminActiveGoogleWebSessions = async ({ limit = 200 } = {}) => {
|
|
|
6782
7157
|
const listAdminKnownGoogleUsers = async ({ limit = 200 } = {}) => {
|
|
6783
7158
|
const safeLimit = Math.max(1, Math.min(500, Number(limit || 200)));
|
|
6784
7159
|
const rows = await executeQuery(
|
|
6785
|
-
`SELECT google_sub, owner_jid, email, name, picture_url, created_at, updated_at, last_login_at, last_seen_at
|
|
7160
|
+
`SELECT google_sub, owner_jid, owner_phone, email, name, picture_url, created_at, updated_at, last_login_at, last_seen_at
|
|
6786
7161
|
FROM ${TABLES.STICKER_WEB_GOOGLE_USER}
|
|
6787
7162
|
ORDER BY COALESCE(last_seen_at, last_login_at, updated_at, created_at) DESC
|
|
6788
7163
|
LIMIT ${safeLimit}`,
|
|
@@ -6790,6 +7165,7 @@ const listAdminKnownGoogleUsers = async ({ limit = 200 } = {}) => {
|
|
|
6790
7165
|
return (Array.isArray(rows) ? rows : []).map((row) => ({
|
|
6791
7166
|
google_sub: normalizeGoogleSubject(row.google_sub),
|
|
6792
7167
|
owner_jid: normalizeJid(row.owner_jid) || null,
|
|
7168
|
+
owner_phone: toWhatsAppPhoneDigits(row.owner_phone || row.owner_jid) || null,
|
|
6793
7169
|
email: normalizeEmail(row.email) || null,
|
|
6794
7170
|
name: sanitizeText(row.name || '', 120, { allowEmpty: true }) || null,
|
|
6795
7171
|
picture: String(row.picture_url || '').trim() || null,
|
|
@@ -7307,7 +7683,7 @@ const handleCatalogApiRequest = async (req, res, pathname, url) => {
|
|
|
7307
7683
|
}
|
|
7308
7684
|
|
|
7309
7685
|
if (pathname === `${STICKER_API_BASE_PATH}/me`) {
|
|
7310
|
-
await handleMyProfileRequest(req, res);
|
|
7686
|
+
await handleMyProfileRequest(req, res, url);
|
|
7311
7687
|
return true;
|
|
7312
7688
|
}
|
|
7313
7689
|
|
|
@@ -7453,6 +7829,15 @@ const handleCatalogApiRequest = async (req, res, pathname, url) => {
|
|
|
7453
7829
|
return true;
|
|
7454
7830
|
}
|
|
7455
7831
|
|
|
7832
|
+
if (segments.length === 1 && segments[0] === 'bot-contact') {
|
|
7833
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
7834
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
7835
|
+
return true;
|
|
7836
|
+
}
|
|
7837
|
+
await handleBotContactInfoRequest(req, res);
|
|
7838
|
+
return true;
|
|
7839
|
+
}
|
|
7840
|
+
|
|
7456
7841
|
if (segments[0] === 'admin') {
|
|
7457
7842
|
if (segments.length === 2 && segments[1] === 'overview') {
|
|
7458
7843
|
await handleAdminOverviewRequest(req, res);
|
|
@@ -7624,6 +8009,20 @@ const handleCatalogPageRequest = async (req, res, pathname) => {
|
|
|
7624
8009
|
|
|
7625
8010
|
if (normalizedPath === STICKER_CREATE_WEB_PATH) {
|
|
7626
8011
|
try {
|
|
8012
|
+
const googleSession = await resolveGoogleWebSessionFromRequest(req);
|
|
8013
|
+
if (!googleSession?.ownerJid) {
|
|
8014
|
+
const requestUrl = new URL(req.url || `${STICKER_CREATE_WEB_PATH}/`, SITE_ORIGIN);
|
|
8015
|
+
const nextPath = `${requestUrl.pathname}${requestUrl.search}`;
|
|
8016
|
+
const loginRedirectUrl = new URL(`${STICKER_LOGIN_WEB_PATH}/`, SITE_ORIGIN);
|
|
8017
|
+
loginRedirectUrl.searchParams.set('next', nextPath);
|
|
8018
|
+
|
|
8019
|
+
res.statusCode = 302;
|
|
8020
|
+
res.setHeader('Location', `${loginRedirectUrl.pathname}${loginRedirectUrl.search}`);
|
|
8021
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
8022
|
+
res.end();
|
|
8023
|
+
return;
|
|
8024
|
+
}
|
|
8025
|
+
|
|
7627
8026
|
const html = await renderCreatePackHtml();
|
|
7628
8027
|
sendText(req, res, 200, html, 'text/html; charset=utf-8');
|
|
7629
8028
|
return;
|
|
@@ -7711,6 +8110,7 @@ export const isStickerCatalogEnabled = () => STICKER_CATALOG_ENABLED;
|
|
|
7711
8110
|
export const getStickerCatalogConfig = () => ({
|
|
7712
8111
|
enabled: STICKER_CATALOG_ENABLED,
|
|
7713
8112
|
webPath: STICKER_WEB_PATH,
|
|
8113
|
+
userProfilePath: USER_PROFILE_WEB_PATH,
|
|
7714
8114
|
apiBasePath: STICKER_API_BASE_PATH,
|
|
7715
8115
|
orphanApiPath: STICKER_ORPHAN_API_PATH,
|
|
7716
8116
|
dataPublicPath: STICKER_DATA_PUBLIC_PATH,
|
|
@@ -7734,6 +8134,29 @@ export async function maybeHandleStickerCatalogRequest(req, res, { pathname, url
|
|
|
7734
8134
|
if (!['GET', 'HEAD', 'POST', 'PATCH', 'DELETE'].includes(req.method || '')) return false;
|
|
7735
8135
|
if (maybeRedirectToCanonicalHost(req, res, url)) return true;
|
|
7736
8136
|
|
|
8137
|
+
if (pathname === USER_PROFILE_WEB_PATH || pathname === `${USER_PROFILE_WEB_PATH}/`) {
|
|
8138
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) return false;
|
|
8139
|
+
try {
|
|
8140
|
+
const html = await renderUserDashboardHtml();
|
|
8141
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
8142
|
+
res.setHeader('X-Robots-Tag', 'noindex, nofollow');
|
|
8143
|
+
sendText(req, res, 200, html, 'text/html; charset=utf-8');
|
|
8144
|
+
} catch (error) {
|
|
8145
|
+
if (error?.code === 'ENOENT') {
|
|
8146
|
+
sendJson(req, res, 404, { error: 'Template da pagina de usuario nao encontrado.' });
|
|
8147
|
+
return true;
|
|
8148
|
+
}
|
|
8149
|
+
|
|
8150
|
+
logger.error('Falha ao renderizar pagina de usuario.', {
|
|
8151
|
+
action: 'user_dashboard_page_render_failed',
|
|
8152
|
+
path: pathname,
|
|
8153
|
+
error: error?.message,
|
|
8154
|
+
});
|
|
8155
|
+
sendJson(req, res, 500, { error: 'Falha interna ao renderizar pagina de usuario.' });
|
|
8156
|
+
}
|
|
8157
|
+
return true;
|
|
8158
|
+
}
|
|
8159
|
+
|
|
7737
8160
|
if (pathname === '/sitemap.xml') {
|
|
7738
8161
|
if (!['GET', 'HEAD'].includes(req.method || '')) return false;
|
|
7739
8162
|
try {
|