use user PAT if available
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { getUserAccess, deleteUser } from "../src/common/database.js";
|
||||
import { getUserAccessByKey, deleteUser } from "../src/common/database.js";
|
||||
import axios from "axios";
|
||||
import { logger } from "../src/common/log.js";
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
// get token and private access status
|
||||
const userAccess = await getUserAccess(user_key);
|
||||
const userAccess = await getUserAccessByKey(user_key);
|
||||
if (!userAccess) {
|
||||
res.statusCode = 404;
|
||||
res.send("user not found");
|
||||
|
||||
@@ -87,7 +87,7 @@ export default async (req, res) => {
|
||||
try {
|
||||
let PATsValid = true;
|
||||
try {
|
||||
await retryer(uptimeFetcher, {});
|
||||
await retryer(uptimeFetcher, null, {});
|
||||
} catch (err) {
|
||||
// Resolve eslint no-unused-vars
|
||||
err;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { logger } from "../src/common/log.js";
|
||||
import { getUserAccess } from "../src/common/database.js";
|
||||
import { getUserAccessByKey } from "../src/common/database.js";
|
||||
|
||||
/**
|
||||
* @param {any} req The request.
|
||||
@@ -8,7 +8,7 @@ import { getUserAccess } from "../src/common/database.js";
|
||||
export default async (req, res) => {
|
||||
const { user_key } = req.query;
|
||||
try {
|
||||
const result = await getUserAccess(user_key);
|
||||
const result = await getUserAccessByKey(user_key);
|
||||
|
||||
if (!result) {
|
||||
res.statusCode = 404;
|
||||
|
||||
@@ -197,7 +197,7 @@ export async function deleteUser(userKey) {
|
||||
* @param {string} userKey user key of the user to fetch information for
|
||||
* @returns {Promise<{token: string, privateAccess: boolean} | null>} token and private access status, or null if user not found
|
||||
*/
|
||||
export async function getUserAccess(userKey) {
|
||||
export async function getUserAccessByKey(userKey) {
|
||||
if (!pool) {
|
||||
return null;
|
||||
}
|
||||
@@ -225,3 +225,38 @@ export async function getUserAccess(userKey) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches token and private access status for a given username.
|
||||
*
|
||||
* @param {string} userName GitHub username of the user to fetch information for
|
||||
* @returns {Promise<{token: string, privateAccess: boolean} | null>} token and private access status, or null if user not found
|
||||
*/
|
||||
export async function getUserAccessByName(userName) {
|
||||
if (!pool) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const query = `
|
||||
SELECT access_token, private_access
|
||||
FROM authenticated_users
|
||||
WHERE user_id = $1
|
||||
LIMIT 1
|
||||
`;
|
||||
try {
|
||||
const { rows } = await pool.query(query, [userName]);
|
||||
if (rows.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
token: rows[0].access_token,
|
||||
privateAccess: rows[0].private_access
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code === "42P01") {
|
||||
return null;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,12 @@
|
||||
|
||||
import { CustomError } from "./error.js";
|
||||
import { logger } from "./log.js";
|
||||
import { getUserAccessByKey, getUserAccessByName } from "./database.js";
|
||||
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
// Script variables.
|
||||
|
||||
// Count the number of GitHub API tokens available.
|
||||
const PATs = Object.keys(process.env).filter((key) =>
|
||||
/PAT_\d*$/.exec(key),
|
||||
).length;
|
||||
const RETRIES = process.env.NODE_ENV === "test" ? 7 : PATs;
|
||||
|
||||
/**
|
||||
* @typedef {import("axios").AxiosResponse} AxiosResponse Axios response.
|
||||
* @typedef {(variables: any, token: string, retriesForTests?: number) => Promise<AxiosResponse>} FetcherFunction Fetcher function.
|
||||
@@ -24,22 +17,39 @@ const RETRIES = process.env.NODE_ENV === "test" ? 7 : PATs;
|
||||
* Try to execute the fetcher function until it succeeds or the max number of retries is reached.
|
||||
*
|
||||
* @param {FetcherFunction} fetcher The fetcher function.
|
||||
* @param username GitHub username of the user whose PAT to use, if available
|
||||
* @param {any} variables Object with arguments to pass to the fetcher function.
|
||||
* @returns {Promise<any>} The response from the fetcher function.
|
||||
*/
|
||||
const retryer = async (fetcher, variables) => {
|
||||
const retryer = async (fetcher, username, variables) => {
|
||||
let userPAT;
|
||||
if (username) {
|
||||
userPAT = await getUserAccessByName(username);
|
||||
}
|
||||
|
||||
let PATs;
|
||||
if (userPAT) {
|
||||
PATs = [userPAT.token];
|
||||
} else {
|
||||
// Count the number of GitHub API tokens available.
|
||||
PATs = Object.keys(process.env).filter((key) =>
|
||||
/PAT_\d*$/.exec(key),
|
||||
);
|
||||
}
|
||||
const RETRIES = process.env.NODE_ENV === "test" ? 7 : PATs.length;
|
||||
|
||||
if (!RETRIES) {
|
||||
throw new CustomError("No GitHub API tokens found", CustomError.NO_TOKENS);
|
||||
}
|
||||
const startPAT = getRandomInt(PATs);
|
||||
const startPAT = getRandomInt(PATs.length);
|
||||
|
||||
for (let retries = 0; retries < RETRIES; retries++) {
|
||||
const currentPAT = ((startPAT + retries) % PATs) + 1;
|
||||
const currentPAT = ((startPAT + retries) % PATs.length);
|
||||
try {
|
||||
let response = await fetcher(
|
||||
variables,
|
||||
// @ts-ignore
|
||||
process.env[`PAT_${currentPAT}`],
|
||||
PATs[currentPAT],
|
||||
// used in tests for faking rate limit
|
||||
retries,
|
||||
);
|
||||
@@ -89,5 +99,5 @@ const retryer = async (fetcher, variables) => {
|
||||
);
|
||||
};
|
||||
|
||||
export { retryer, RETRIES };
|
||||
export { retryer };
|
||||
export default retryer;
|
||||
|
||||
@@ -90,7 +90,7 @@ const fetchGist = async (id) => {
|
||||
if (!id) {
|
||||
throw new MissingParamError(["id"], "/api/gist?id=GIST_ID");
|
||||
}
|
||||
const res = await retryer(fetcher, { gistName: id });
|
||||
const res = await retryer(fetcher, null, { gistName: id });
|
||||
if (res.data.errors) {
|
||||
throw new Error(res.data.errors[0].message);
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ const fetchRepo = async (
|
||||
throw new MissingParamError(["repo"], urlExample);
|
||||
}
|
||||
|
||||
let res = await retryer(fetcher, { login: owner, repo: reponame });
|
||||
let res = await retryer(fetcher, username, { login: owner, repo: reponame });
|
||||
|
||||
const data = res.data.data;
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ const statsFetcher = async ({
|
||||
startTime,
|
||||
ownerAffiliations,
|
||||
};
|
||||
let res = await retryer(fetcher, variables);
|
||||
let res = await retryer(fetcher, username, variables);
|
||||
if (res.data.errors) {
|
||||
return res;
|
||||
}
|
||||
@@ -221,7 +221,7 @@ const totalItemsFetcher = async (username, repo, owner, type, filter) => {
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await retryer(fetchTotalItems, {
|
||||
res = await retryer(fetchTotalItems, username, {
|
||||
login: username,
|
||||
repo,
|
||||
owner,
|
||||
|
||||
@@ -73,7 +73,7 @@ const fetchTopLanguages = async (
|
||||
}
|
||||
ownerAffiliations = parseOwnerAffiliations(ownerAffiliations);
|
||||
|
||||
const res = await retryer(fetcher, { login: username, ownerAffiliations });
|
||||
const res = await retryer(fetcher, username, { login: username, ownerAffiliations });
|
||||
|
||||
if (res.data.errors) {
|
||||
logger.error(res.data.errors);
|
||||
|
||||
Reference in New Issue
Block a user