diff --git a/src/serverScripts/analytics.ts b/src/serverScripts/analytics.ts index 2e651450..5e558352 100644 --- a/src/serverScripts/analytics.ts +++ b/src/serverScripts/analytics.ts @@ -249,7 +249,7 @@ async function trackGenericProperty(key: string, value: string, data2: string): } } -type RedditApiUsageRecord = { clientId: string, timeMillisUtc: number, endpoint: string, api?: string }; +type RedditApiUsageRecord = { clientId: string, timeMillisUtc: number, endpoint: string, api?: string, used?: number, remaining?: number }; async function trackRedditApiUsage(records: RedditApiUsageRecord[]): Promise { const connection = await getConnection(); if (connection === null) @@ -258,9 +258,9 @@ async function trackRedditApiUsage(records: RedditApiUsageRecord[]): Promise `(${connection.escape(record.clientId)}, ${connection.escape(record.timeMillisUtc)}, ${connection.escape(record.endpoint)}, ${connection.escape(record.api)})`) + .map(record => `(${connection.escape(record.clientId)}, ${connection.escape(record.timeMillisUtc)}, ${connection.escape(record.endpoint)}, ${connection.escape(record.api)}, ${connection.escape(record.used)}, ${connection.escape(record.remaining)})`) .join(",") } `); @@ -431,7 +431,9 @@ analyticsRouter.post("/redditApiUsage", safeExcAsync(async (req, res) => { typeof record.clientId === "string" && typeof record.timeMillisUtc === "number" && record.timeMillisUtc >= 0 && - typeof record.endpoint === "string" + typeof record.endpoint === "string" && + (!("used" in record) || typeof record.used === "number") && + (!("remaining" in record) || typeof record.remaining === "number") ) } if (!(records instanceof Array) || !records.every(isRecordValid)) { @@ -441,6 +443,8 @@ analyticsRouter.post("/redditApiUsage", safeExcAsync(async (req, res) => { for (const record of records) { record.endpoint = record.endpoint.slice(0, 64); record.api ??= null; + record.used ??= null; + record.remaining ??= null; } try { await trackRedditApiUsage(records); diff --git a/src/static/scripts/api/redditApi.ts b/src/static/scripts/api/redditApi.ts index 918a20bc..cb077d86 100644 --- a/src/static/scripts/api/redditApi.ts +++ b/src/static/scripts/api/redditApi.ts @@ -17,7 +17,7 @@ import { RedditSubredditObj, RedditUserObj } from "../types/redditTypes"; -import {isObjectEmpty, randomString, splitPathQuery, throttle} from "../utils/utils"; +import {isObjectEmpty, randomString, splitPathQuery, strToNumNonNan, throttle} from "../utils/utils"; import {SortPostsOrder, UserSection} from "../types/misc"; import {onApiUsage} from "./redditApiUsageTracking"; @@ -42,7 +42,6 @@ export async function redditApiRequest(pathAndQuery, params: string[][] | any, r async function oauth2Request(pathAndQuery, params: string[][] | any, options: RequestInit, attempt = 0) { pathAndQuery = fixUrl(pathAndQuery); const [path, query] = splitPathQuery(pathAndQuery); - onApiUsage(path, "reddit"); let fetchOptions: RequestInit = { ...options, @@ -70,6 +69,7 @@ async function oauth2Request(pathAndQuery, params: string[][] | any, options: Re try { const response = await fetch(`https://oauth.reddit.com${ path }?${ useUrlParams ? parameters.toString() : "" }`, fetchOptions); const responseText = await response.text(); + onApiUsage(path, "reddit", true, strToNumNonNan(response.headers.get("x-ratelimit-used")), strToNumNonNan(response.headers.get("x-ratelimit-remaining"))); rateLimitCheck(response.headers); return responseText ? JSON.parse(responseText) : {}; } catch (e) { diff --git a/src/static/scripts/api/redditApiUsageTracking.ts b/src/static/scripts/api/redditApiUsageTracking.ts index 63b054be..703edaa0 100644 --- a/src/static/scripts/api/redditApiUsageTracking.ts +++ b/src/static/scripts/api/redditApiUsageTracking.ts @@ -3,7 +3,7 @@ import Users from "../multiUser/userManagement"; import {RedditApiUsageRecord} from "../types/misc"; import {trackApiUsage} from "./photonApi"; -export async function onApiUsage(endpoint: string, api: string, generalize = true) { +export async function onApiUsage(endpoint: string, api: string, generalize = true, used: number|undefined = undefined, remaining: number|undefined = undefined) { if (generalize) endpoint = generalizeEndpoint(endpoint); const newRecord: RedditApiUsageRecord = { @@ -11,6 +11,8 @@ export async function onApiUsage(endpoint: string, api: string, generalize = tru timeMillisUtc: Date.now(), endpoint, api, + used, + remaining }; let pendingUsages = Users.global.d.pendingRedditApiUsages; pendingUsages = [...pendingUsages, newRecord]; diff --git a/src/static/scripts/types/misc.ts b/src/static/scripts/types/misc.ts index 43317221..cf9839c2 100644 --- a/src/static/scripts/types/misc.ts +++ b/src/static/scripts/types/misc.ts @@ -172,4 +172,6 @@ export interface RedditApiUsageRecord { timeMillisUtc: number; endpoint: string; api: string; + used?: number; + remaining?: number; } diff --git a/src/static/scripts/utils/utils.ts b/src/static/scripts/utils/utils.ts index dbe6c350..76b3fff3 100644 --- a/src/static/scripts/utils/utils.ts +++ b/src/static/scripts/utils/utils.ts @@ -673,3 +673,8 @@ export function fromBase36(numB36: string): number { } return num; } + +export function strToNumNonNan(numStr: string, fallback: T = undefined): number|T { + const num = Number(numStr); + return isNaN(num) ? fallback : num; +}