diff --git a/app.js b/app.js index a1977a9..79c8a03 100644 --- a/app.js +++ b/app.js @@ -772,6 +772,9 @@ app.post('/api/user', async (req, res) => { case 'friendsList': data = await fetchWithTimeout(() => API.Me.friendsList()); break; + case 'userInfo': + data = await fetchWithTimeout(() => API.Me.userInfo()); + break; // case "settings": // data = await fetchWithTimeout(() => // API.Me.settings(username, platform) @@ -807,6 +810,105 @@ app.post('/api/user', async (req, res) => { } }); +// API endpoint for CDN-related API calls +app.post('/api/cdn', async (req, res) => { + logger.debug('Received request for /api/cdn'); + logger.debug( + `Request IP: ${ + req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress + }` + ); + logger.debug( + `Request JSON: ${JSON.stringify({ + cdnCall: req.body.cdnCall, + })}` + ); + + try { + let { ssoToken, cdnCall } = req.body; + + /* + logger.debug( + `Request details - User Call: ${cdnCall}` + ); + + logger.debug("=== USER DATA REQUEST ==="); + logger.debug(`CDN Call: ${cdnCall}`); + logger.debug("========================="); */ + + const defaultToken = demoTracker.getDefaultSsoToken(); + if (!ssoToken && defaultToken) { + ssoToken = defaultToken; + logger.info('Using default SSO token for demo mode'); + } else if (!ssoToken) { + return res.status(200).json({ + status: 'error', + message: 'SSO Token is required', + timestamp: global.Utils.toIsoString(new Date()), + }); + } + + try { + await ensureLogin(ssoToken); + } catch (loginError) { + return res.status(200).json({ + status: 'error', + error_type: 'LoginError', + message: 'SSO token login failed', + details: loginError.message || 'Unknown login error', + timestamp: global.Utils.toIsoString(new Date()), + }); + } + + // Create a wrapped function for each API call to handle timeout + const fetchWithTimeout = async (apiFn) => { + return Promise.race([ + apiFn(), + timeoutPromise(30000), // 30 second timeout + ]); + }; + + try { + logger.debug(`Attempting to fetch CDN data for ${cdnCall}`); + let data; + + // Fetch user data based on cdnCall + switch (cdnCall) { + case 'accolades': + data = await fetchWithTimeout(() => API.CDN.accolades()); + break; + case 'allCDNData': + data = await fetchWithTimeout(() => API.CDN.allCDNData()); + break; + + default: + return res.status(200).json({ + status: 'error', + message: 'Invalid user API call selected', + timestamp: global.Utils.toIsoString(new Date()), + }); + } + + demoTracker.incrementDemoCounter(req, ssoToken); + + return res.json({ + // status: "success", + data: data, + timestamp: global.Utils.toIsoString(new Date()), + }); + } catch (apiError) { + return handleApiError(apiError, res); + } + } catch (serverError) { + return res.status(200).json({ + status: 'server_error', + message: 'The server encountered an unexpected error', + error_details: serverError.message || 'Unknown server error', + timestamp: global.Utils.toIsoString(new Date()), + }); + } +}); + // API endpoint for fuzzy search app.post('/api/search', async (req, res) => { logger.debug('Received request for /api/search'); diff --git a/src/index.html b/src/index.html index 199586e..8e3e690 100644 --- a/src/index.html +++ b/src/index.html @@ -68,6 +68,7 @@
Player
Matches
User Info
+
CDN
Search
@@ -285,7 +286,9 @@
- +
@@ -355,16 +358,15 @@
@@ -413,6 +415,60 @@
+ +
+
+ + +
+ + + +
+

Authentication Setup

+

Obtaining Your SSO Token

+
    +
  1. + Log in to + Call of Duty +
  2. +
  3. Open developer tools (F12 or right-click → Inspect)
  4. +
  5. + Navigate to: ApplicationStorage → + Cookieshttps://profile.callofduty.com +
  6. +
  7. Copy the value of ACT_SSO_COOKIE
  8. +
  9. Paste this value into SSO Token
  10. +
+ +

Changing Account API Privacy Settings

+
    +
  1. + Log in to + Call of Duty +
  2. +
  3. Navigate to the Privacy & Security tab
  4. +
  5. + Scroll down to the Game Data and Profile Privacy section +
  6. +
  7. + Set the following options to "ALL": +
      +
    • Game Tag Searchable
    • +
    • Game Play Data
    • +
    +
  8. +
+
+
+
diff --git a/src/js/frontend.js b/src/js/frontend.js index 1481237..31f313e 100644 --- a/src/js/frontend.js +++ b/src/js/frontend.js @@ -272,6 +272,9 @@ function triggerActiveTabButton() { case 'user': document.getElementById('fetchUserInfo').click(); break; + case 'cdn': + document.getElementById('fetchCDNData').click(); + break; case 'other': document.getElementById('fuzzySearch').click(); break; @@ -799,6 +802,22 @@ document.getElementById('fetchUserInfo').addEventListener('click', async () => { }); }); +// Fetch CDN Data +document.getElementById('fetchCDNData').addEventListener('click', async () => { + const ssoToken = document.getElementById('ssoToken').value.trim(); + const cdnCall = document.getElementById('cdnCall').value; + + const sanitize = document.getElementById('sanitizeOption').checked; + const replaceKeys = document.getElementById('replaceKeysOption').checked; + + await window.backendAPI.fetchData('/api/cdn', { + ssoToken, + cdnCall, + sanitize, + replaceKeys, + }); +}); + // Fuzzy search document.getElementById('fuzzySearch').addEventListener('click', async () => { const username = document.getElementById('searchUsername').value.trim(); diff --git a/src/js/index.ts b/src/js/index.ts index 4771b2b..afc6898 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -1589,8 +1589,22 @@ const CDN = new DB(); const Misc = new ALT(); export { - CDN, ColdWar, disableDebugMode, enableDebugMode, friendActions, login, Me, Misc, ModernWarfare, + CDN, + ColdWar, + disableDebugMode, + enableDebugMode, + friendActions, + login, + Me, + Misc, + ModernWarfare, ModernWarfare2, - ModernWarfare3, platforms, Store, telescopeLogin, Vanguard, Warzone, Warzone2, WarzoneMobile + ModernWarfare3, + platforms, + Store, + telescopeLogin, + Vanguard, + Warzone, + Warzone2, + WarzoneMobile, }; - diff --git a/src/js/localStorage.js b/src/js/localStorage.js index d1db4ac..6798fa8 100644 --- a/src/js/localStorage.js +++ b/src/js/localStorage.js @@ -5,7 +5,7 @@ document.addEventListener('DOMContentLoaded', function () { { id: 'username', key: 'cod_username' }, { id: 'platform', key: 'cod_platform' }, { id: 'game', key: 'cod_game' }, - { id: 'apiCall', key: 'cod_apiCall' }, + { id: 'userCall', key: 'cod_userCall' }, // Matches tab { id: 'matchUsername', key: 'cod_matchUsername' }, diff --git a/src/js/test.js b/src/js/test.js index 2c41378..e4f50a3 100644 --- a/src/js/test.js +++ b/src/js/test.js @@ -9,8 +9,8 @@ import { } from './index.js'; import fs from 'fs'; -// Me { communityMapDataForMapMode(game, platform, mode, mapID, gamemode), userInfo() }; -// CDN { accolades(), allCDNData() }; +// Me { communityMapDataForMapMode(game, platform, mode, mapID, gamemode) }; + // ModernWarfare: { bundleInfo(game, platform, lookupType, gamertag), matchHeatMap(game, platform, matchID), bpProg(game, platform, lookupType, gamertag) }; // ColdWar: { bundleInfo(game, platform, lookupType, gamertag), matchHeatMap(game, platform, matchID), bpProg(game, platform, lookupType, gamertag) }; // Vanguard: { bundleInfo(game, platform, lookupType, gamertag), matchHeatMap(game, platform, matchID), bpProg(game, platform, lookupType, gamertag) };