Skip to content

Commit

Permalink
Merge pull request anuraghazra#1 from meyer-pidiache/base
Browse files Browse the repository at this point in the history
Pull from fork
  • Loading branch information
meyer-pidiache authored Apr 11, 2023
2 parents 7e695e4 + 29c44eb commit 85526f3
Show file tree
Hide file tree
Showing 40 changed files with 1,521 additions and 333 deletions.
1 change: 1 addition & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
themes: themes/index.js
doc-translation: docs/*
card-i18n: src/translations.js
documentation: readme.md
10 changes: 10 additions & 0 deletions .github/workflows/deploy-prep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import os

file = open('./vercel.json', 'r')
str = file.read()
file = open('./vercel.json', 'w')

str = str.replace('"maxDuration": 10', '"maxDuration": 30')

file.write(str)
file.close()
20 changes: 20 additions & 0 deletions .github/workflows/deploy-prep.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Deployment Prep
on:
workflow_dispatch:
push:
branches:
- master

jobs:
config:
if: github.repository == 'anuraghazra/github-readme-stats'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deployment Prep
run: python ./.github/workflows/deploy-prep.py
- uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: vercel
create_branch: true
push_options: "--force"
1 change: 1 addition & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
jobs:
e2eTests:
if:
github.repository == 'meyer-pidiache/github-readme-stats' &&
github.event_name == 'deployment_status' &&
github.event.deployment_status.state == 'success'
name: Perform 2e2 tests
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/empty-issues-closer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:

jobs:
closeEmptyIssuesAndTemplates:
if: github.repository == 'anuraghazra/github-readme-stats'
name: Close empty issues
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/generate-theme-doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ jobs:
node-version: ${{ matrix.node-version }}
cache: npm

# Fix the unsafe repo error which was introduced by the CVE-2022-24765 git patches.
- name: Fix unsafe repo error
run: git config --global --add safe.directory ${{ github.workspace }}

- name: npm install, generate readme
run: |
npm ci
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/label-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:

jobs:
triage:
if: github.repository == 'meyer-pidiache/github-readme-stats'
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/stale-theme-pr-closer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:

jobs:
closeOldThemePrs:
if: github.repository == 'meyer-pidiache/github-readme-stats'
name: Close stale 'invalid' theme PRs
runs-on: ubuntu-latest
strategy:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/top-issues-dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:

jobs:
showAndLabelTopIssues:
if: github.repository == 'anuraghazra/github-readme-stats'
name: Update top issues Dashboard.
runs-on: ubuntu-latest
steps:
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/update-langs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Update supported languages
on:
schedule:
- cron: "0 0 */30 * *"

jobs:
updateLanguages:
if: github.repository == 'anuraghazra/github-readme-stats'
name: Update supported languages
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]

steps:
- uses: actions/checkout@v3

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: npm

- name: Install dependencies
run: npm ci
env:
CI: true

- name: Run update-languages-json.js script
run: npm run generate-langs-json

- name: Create Pull Request if upstream language file is changed
uses: peter-evans/create-pull-request@v4
with:
commit-message: "refactor: update languages JSON"
branch: "update_langs/patch"
delete-branch: true
title: Update languages JSON
body:
"The
[update-langs](https://github.com/anuraghazra/github-readme-stats/actions/workflows/update-langs.yaml)
action found new/updated languages in the [upstream languages JSON
file](https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml)."
labels: "ci, lang-card"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ vercel_token
# IDE
.vscode
*.code-workspace

.vercel
2 changes: 2 additions & 0 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default async (req, res) => {
locale,
disable_animations,
border_radius,
number_format,
border_color,
} = req.query;
res.setHeader("Content-Type", "image/svg+xml");
Expand Down Expand Up @@ -88,6 +89,7 @@ export default async (req, res) => {
custom_title,
border_radius,
border_color,
number_format,
locale: locale ? locale.toLowerCase() : null,
disable_animations: parseBoolean(disable_animations),
}),
Expand Down
139 changes: 139 additions & 0 deletions api/status/pat-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* @file Contains a simple cloud function that can be used to check which PATs are no
* longer working. It returns a list of valid PATs, expired PATs and PATs with errors.
*
* @description This function is currently rate limited to 1 request per 5 minutes.
*/

import { logger, request, dateDiff } from "../../src/common/utils.js";
export const RATE_LIMIT_SECONDS = 60 * 5; // 1 request per 5 minutes

/**
* Simple uptime check fetcher for the PATs.
*
* @param {import('axios').AxiosRequestHeaders} variables
* @param {string} token
*/
const uptimeFetcher = (variables, token) => {
return request(
{
query: `
query {
rateLimit {
remaining
resetAt
},
}`,
variables,
},
{
Authorization: `bearer ${token}`,
},
);
};

const getAllPATs = () => {
return Object.keys(process.env).filter((key) => /PAT_\d*$/.exec(key));
};

/**
* Check whether any of the PATs is expired.
*/
const getPATInfo = async (fetcher, variables) => {
const details = {};
const PATs = getAllPATs();

for (const pat of PATs) {
try {
const response = await fetcher(variables, process.env[pat]);
const errors = response.data.errors;
const hasErrors = Boolean(errors);
const errorType = errors?.[0]?.type;
const isRateLimited =
(hasErrors && errorType === "RATE_LIMITED") ||
response.data.data?.rateLimit?.remaining === 0;

// Store PATs with errors.
if (hasErrors && errorType !== "RATE_LIMITED") {
details[pat] = {
status: "error",
error: {
type: errors[0].type,
message: errors[0].message,
},
};
continue;
} else if (isRateLimited) {
const date1 = new Date();
const date2 = new Date(response.data?.data?.rateLimit?.resetAt);
details[pat] = {
status: "exhausted",
remaining: 0,
resetIn: dateDiff(date2, date1) + " minutes",
};
} else {
details[pat] = {
status: "valid",
remaining: response.data.data.rateLimit.remaining,
};
}
} catch (err) {
// Store the PAT if it is expired.
const errorMessage = err.response?.data?.message?.toLowerCase();
if (errorMessage === "bad credentials") {
details[pat] = {
status: "expired",
};
} else if (errorMessage === "sorry. your account was suspended.") {
details[pat] = {
status: "suspended",
};
} else {
throw err;
}
}
}

const filterPATsByStatus = (status) => {
return Object.keys(details).filter((pat) => details[pat].status === status);
};

const sortedDetails = Object.keys(details)
.sort()
.reduce((obj, key) => {
obj[key] = details[key];
return obj;
}, {});

return {
validPATs: filterPATsByStatus("valid"),
expiredPATs: filterPATsByStatus("expired"),
exhaustedPATs: filterPATsByStatus("exhausted"),
suspendedPATs: filterPATsByStatus("suspended"),
errorPATs: filterPATsByStatus("error"),
details: sortedDetails,
};
};

/**
* Cloud function that returns information about the used PATs.
*/
export default async (_, res) => {
res.setHeader("Content-Type", "application/json");
try {
// Add header to prevent abuse.
const PATsInfo = await getPATInfo(uptimeFetcher, {});
if (PATsInfo) {
res.setHeader(
"Cache-Control",
`max-age=0, s-maxage=${RATE_LIMIT_SECONDS}`,
);
}
res.send(JSON.stringify(PATsInfo, null, 2));
} catch (err) {
// Throw error if something went wrong.
logger.error(err);
res.setHeader("Cache-Control", "no-store");
res.send("Something went wrong: " + err.message);
}
};
103 changes: 103 additions & 0 deletions api/status/up.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @file Contains a simple cloud function that can be used to check if the PATs are still
* functional.
*
* @description This function is currently rate limited to 1 request per 5 minutes.
*/

import retryer from "../../src/common/retryer.js";
import { logger, request } from "../../src/common/utils.js";

export const RATE_LIMIT_SECONDS = 60 * 5; // 1 request per 5 minutes

/**
* Simple uptime check fetcher for the PATs.
*
* @param {import('axios').AxiosRequestHeaders} variables
* @param {string} token
*/
const uptimeFetcher = (variables, token) => {
return request(
{
query: `
query {
rateLimit {
remaining
}
}
`,
variables,
},
{
Authorization: `bearer ${token}`,
},
);
};

/**
* Creates Json response that can be used for shields.io dynamic card generation.
*
* @param {*} up Whether the PATs are up or not.
* @returns Dynamic shields.io JSON response object.
*
* @see https://shields.io/endpoint.
*/
const shieldsUptimeBadge = (up) => {
const schemaVersion = 1;
const isError = true;
const label = "Public Instance";
const message = up ? "up" : "down";
const color = up ? "brightgreen" : "red";
return {
schemaVersion,
label,
message,
color,
isError,
};
};

/**
* Cloud function that returns whether the PATs are still functional.
*/
export default async (req, res) => {
let { type } = req.query;
type = type ? type.toLowerCase() : "boolean";

res.setHeader("Content-Type", "application/json");

try {
let PATsValid = true;
try {
await retryer(uptimeFetcher, {});
} catch (err) {
PATsValid = false;
}

if (PATsValid) {
res.setHeader(
"Cache-Control",
`max-age=0, s-maxage=${RATE_LIMIT_SECONDS}`,
);
} else {
res.setHeader("Cache-Control", "no-store");
}

switch (type) {
case "shields":
res.send(shieldsUptimeBadge(PATsValid));
break;
case "json":
res.send({ up: PATsValid });
break;
default:
res.send(PATsValid);
break;
}
} catch (err) {
// Return fail boolean if something went wrong.
logger.error(err);
res.setHeader("Cache-Control", "no-store");
res.send("Something went wrong: " + err.message);
}
};
Loading

0 comments on commit 85526f3

Please sign in to comment.