mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
171 lines
5.3 KiB
TypeScript
171 lines
5.3 KiB
TypeScript
import { EVERY_HOUR, EVERY_FIVE_MINUTE } from "Common/Utils/CronTime";
|
|
import { IsDevelopment } from "Common/Server/EnvironmentConfig";
|
|
import BasicCron from "Common/Server/Utils/BasicCron";
|
|
import logger from "Common/Server/Utils/Logger";
|
|
import LocalCache from "Common/Server/Infrastructure/LocalCache";
|
|
import URL from "Common/Types/API/URL";
|
|
import API from "Common/Utils/API";
|
|
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
|
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
|
import { JSONObject } from "Common/Types/JSON";
|
|
|
|
const GITHUB_STATS_NAMESPACE: string = "home";
|
|
const GITHUB_CONTRIBUTORS_CACHE_KEY: string = "githubContributors";
|
|
const GITHUB_COMMITS_CACHE_KEY: string = "githubCommits";
|
|
|
|
// Default fallback values (used if API fails)
|
|
const DEFAULT_CONTRIBUTORS: number = 80;
|
|
const DEFAULT_COMMITS: number = 29000;
|
|
|
|
export const getGitHubContributorsCount: () => number = (): number => {
|
|
if (
|
|
!LocalCache.hasValue(GITHUB_STATS_NAMESPACE, GITHUB_CONTRIBUTORS_CACHE_KEY)
|
|
) {
|
|
return DEFAULT_CONTRIBUTORS;
|
|
}
|
|
|
|
return (
|
|
LocalCache.getNumber(
|
|
GITHUB_STATS_NAMESPACE,
|
|
GITHUB_CONTRIBUTORS_CACHE_KEY,
|
|
) || DEFAULT_CONTRIBUTORS
|
|
);
|
|
};
|
|
|
|
export const getGitHubCommitsCount: () => number = (): number => {
|
|
if (!LocalCache.hasValue(GITHUB_STATS_NAMESPACE, GITHUB_COMMITS_CACHE_KEY)) {
|
|
return DEFAULT_COMMITS;
|
|
}
|
|
|
|
return (
|
|
LocalCache.getNumber(GITHUB_STATS_NAMESPACE, GITHUB_COMMITS_CACHE_KEY) ||
|
|
DEFAULT_COMMITS
|
|
);
|
|
};
|
|
|
|
export const formatCount: (count: number) => string = (
|
|
count: number,
|
|
): string => {
|
|
if (count >= 1000) {
|
|
const thousands: number = Math.ceil(count / 1000);
|
|
return `${thousands}k+`;
|
|
}
|
|
return `${count}+`;
|
|
};
|
|
|
|
const fetchGitHubStats: () => Promise<void> = async (): Promise<void> => {
|
|
try {
|
|
// Fetch contributors count
|
|
let contributorsCount: number = 0;
|
|
let hasMoreContributors: boolean = true;
|
|
let pageNumber: number = 1;
|
|
|
|
while (hasMoreContributors) {
|
|
const response: HTTPResponse<Array<JSONObject>> | HTTPErrorResponse =
|
|
await API.get<Array<JSONObject>>({
|
|
url: URL.fromString(
|
|
`https://api.github.com/repos/oneuptime/oneuptime/contributors?per_page=100&page=${pageNumber}`,
|
|
),
|
|
headers: {
|
|
Accept: "application/vnd.github.v3+json",
|
|
"User-Agent": "OneUptime-Home",
|
|
},
|
|
});
|
|
|
|
if (response instanceof HTTPErrorResponse) {
|
|
logger.error("FetchGitHubStats: Failed to fetch contributors");
|
|
break;
|
|
}
|
|
|
|
const contributors: Array<JSONObject> =
|
|
response.data as Array<JSONObject>;
|
|
contributorsCount += contributors.length;
|
|
|
|
if (contributors.length < 100) {
|
|
hasMoreContributors = false;
|
|
}
|
|
pageNumber++;
|
|
|
|
// Safety limit to prevent infinite loops
|
|
if (pageNumber > 50) {
|
|
hasMoreContributors = false;
|
|
}
|
|
}
|
|
|
|
if (contributorsCount > 0) {
|
|
LocalCache.setNumber(
|
|
GITHUB_STATS_NAMESPACE,
|
|
GITHUB_CONTRIBUTORS_CACHE_KEY,
|
|
contributorsCount,
|
|
);
|
|
logger.debug(
|
|
`FetchGitHubStats: Updated contributors count to ${contributorsCount}`,
|
|
);
|
|
}
|
|
|
|
// Fetch commits count using pagination header
|
|
const commitsResponse: HTTPResponse<Array<JSONObject>> | HTTPErrorResponse =
|
|
await API.get<Array<JSONObject>>({
|
|
url: URL.fromString(
|
|
"https://api.github.com/repos/oneuptime/oneuptime/commits?sha=master&per_page=1&page=1",
|
|
),
|
|
headers: {
|
|
Accept: "application/vnd.github.v3+json",
|
|
"User-Agent": "OneUptime-Home",
|
|
},
|
|
});
|
|
|
|
if (!(commitsResponse instanceof HTTPErrorResponse)) {
|
|
/*
|
|
* Parse the Link header to get total commits count
|
|
* Format: '<https://api.github.com/repositories/xxx/commits?sha=master&per_page=1&page=2>; rel="next", <https://api.github.com/repositories/xxx/commits?sha=master&per_page=1&page=22486>; rel="last"'
|
|
*/
|
|
const link: string | undefined = commitsResponse.headers["link"];
|
|
if (link) {
|
|
const lastLink: string | undefined = link
|
|
.split(",")
|
|
.find((s: string) => {
|
|
return s.includes('rel="last"');
|
|
});
|
|
if (lastLink) {
|
|
const urlMatch: RegExpMatchArray | null = lastLink.match(/<([^>]+)>/);
|
|
if (urlMatch && urlMatch[1]) {
|
|
const url: URL = URL.fromString(urlMatch[1]);
|
|
const commitsCount: number = Number.parseInt(
|
|
url.getQueryParam("page") as string,
|
|
10,
|
|
);
|
|
|
|
if (commitsCount > 0) {
|
|
LocalCache.setNumber(
|
|
GITHUB_STATS_NAMESPACE,
|
|
GITHUB_COMMITS_CACHE_KEY,
|
|
commitsCount,
|
|
);
|
|
logger.debug(
|
|
`FetchGitHubStats: Updated commits count to ${commitsCount}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
logger.error("FetchGitHubStats: Error fetching GitHub stats");
|
|
logger.error(error);
|
|
}
|
|
};
|
|
|
|
BasicCron({
|
|
jobName: "Home:FetchGitHubStats",
|
|
options: {
|
|
schedule: IsDevelopment ? EVERY_FIVE_MINUTE : EVERY_HOUR,
|
|
runOnStartup: true,
|
|
},
|
|
runFunction: async () => {
|
|
logger.debug("FetchGitHubStats: Start");
|
|
await fetchGitHubStats();
|
|
logger.debug("FetchGitHubStats: End");
|
|
},
|
|
});
|