feat: implement userInfo() and cdn*() API calls into UI

This commit is contained in:
Rim 2025-04-19 21:56:33 -04:00
parent 1ddb12c6e8
commit 660bfb058d
6 changed files with 204 additions and 13 deletions

102
app.js
View File

@ -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');

View File

@ -68,6 +68,7 @@
<div class="tab active" data-tab="stats">Player</div>
<div class="tab" data-tab="matches">Matches</div>
<div class="tab" data-tab="user">User Info</div>
<div class="tab" data-tab="cdn">CDN</div>
<div class="tab" data-tab="other">Search</div>
</div>
@ -285,7 +286,9 @@
<div class="button-group">
<button id="fetchMatches">Fetch Recent Match History</button>
<button id="fetchMatchInfo">Fetch Match Details</button>
<button id="fetchMatchInfo">
Fetch Match Details (Requires MatchID)
</button>
</div>
<div id="tutorial" class="tutorial">
@ -355,16 +358,15 @@
<div class="form-group">
<label for="userCall">API Request:</label>
<select id="userCall">
<option value="codPoints">COD Points</option>
<option value="connectedAccounts">Connected Accounts</option>
<option value="eventFeed">Event Feed (Logged In User Only)</option>
<option value="friendFeed">
Friend Feed (Logged In User Only)
</option>
<option value="userInfo">User Info (Logged In User Only)</option>
<option value="codPoints">COD Points (Logged In User Only)</option>
<option value="connectedAccounts">Connected Accounts (Logged In User Only)</option>
<option value="identities">Identities (Logged In User Only)</option>
<option value="friendsList">
Friends List (Logged In User Only)
</option>
<option value="eventFeed">Event Feed (Logged In User Only)</option>
<option value="friendFeed">Friend Feed</option>
<!-- <option value="settings">Settings</option> -->
</select>
</div>
@ -413,6 +415,60 @@
</div>
</div>
<!-- CDN tab -->
<div class="tab-content" id="cdn-tab">
<div class="form-group">
<label for="cdnCall">API Request:</label>
<select id="cdnCall">
<option value="accolades">Accolade List</option>
<option value="allCDNData">CDN Data List</option>
</select>
</div>
<button id="fetchCDNData">Fetch CDN Content</button>
<div id="tutorial" class="tutorial">
<h2>Authentication Setup</h2>
<h3>Obtaining Your SSO Token</h3>
<ol>
<li>
Log in to
<a href="https://profile.callofduty.com" target="_blank"
>Call of Duty</a
>
</li>
<li>Open developer tools (F12 or right-click → Inspect)</li>
<li>
Navigate to: <b>Application</b><b>Storage</b>
<b>Cookies</b><b>https://profile.callofduty.com</b>
</li>
<li>Copy the value of <code>ACT_SSO_COOKIE</code></li>
<li>Paste this value into SSO Token</li>
</ol>
<h3>Changing Account API Privacy Settings</h3>
<ol>
<li>
Log in to
<a href="https://profile.callofduty.com" target="_blank"
>Call of Duty</a
>
</li>
<li>Navigate to the <b>Privacy & Security</b> tab</li>
<li>
Scroll down to the <b>Game Data and Profile Privacy</b> section
</li>
<li>
Set the following options to "<b>ALL</b>":
<ul>
<li><b>Game Tag Searchable</b></li>
<li><b>Game Play Data</b></li>
</ul>
</li>
</ol>
</div>
</div>
<!-- Search tab -->
<div class="tab-content" id="other-tab">
<div class="form-group">

View File

@ -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();

View File

@ -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,
};

View File

@ -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' },

View File

@ -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) };