forked from mirrored/github-readme-stats
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c504c9338e | ||
|
|
aa2e8ff3c5 |
@@ -1,4 +1,3 @@
|
|||||||
themes: themes/index.js
|
themes: themes/index.js
|
||||||
doc-translation: docs/*
|
doc-translation: docs/*
|
||||||
card-i18n: src/translations.js
|
card-i18n: src/translations.js
|
||||||
documentation: readme.md
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
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()
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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"
|
|
||||||
@@ -5,7 +5,6 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
e2eTests:
|
e2eTests:
|
||||||
if:
|
if:
|
||||||
github.repository == 'anuraghazra/github-readme-stats' &&
|
|
||||||
github.event_name == 'deployment_status' &&
|
github.event_name == 'deployment_status' &&
|
||||||
github.event.deployment_status.state == 'success'
|
github.event.deployment_status.state == 'success'
|
||||||
name: Perform 2e2 tests
|
name: Perform 2e2 tests
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
closeEmptyIssuesAndTemplates:
|
closeEmptyIssuesAndTemplates:
|
||||||
if: github.repository == 'anuraghazra/github-readme-stats'
|
|
||||||
name: Close empty issues
|
name: Close empty issues
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -23,10 +23,6 @@ jobs:
|
|||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: npm
|
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
|
- name: npm install, generate readme
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
npm ci
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
triage:
|
triage:
|
||||||
if: github.repository == 'anuraghazra/github-readme-stats'
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v4
|
- uses: actions/labeler@v4
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
closeOldThemePrs:
|
closeOldThemePrs:
|
||||||
if: github.repository == 'anuraghazra/github-readme-stats'
|
|
||||||
name: Close stale 'invalid' theme PRs
|
name: Close stale 'invalid' theme PRs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
showAndLabelTopIssues:
|
showAndLabelTopIssues:
|
||||||
if: github.repository == 'anuraghazra/github-readme-stats'
|
|
||||||
name: Update top issues Dashboard.
|
name: Update top issues Dashboard.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
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"
|
|
||||||
@@ -10,5 +10,3 @@ vercel_token
|
|||||||
# IDE
|
# IDE
|
||||||
.vscode
|
.vscode
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
.vercel
|
|
||||||
|
|||||||
+1
-5
@@ -47,11 +47,7 @@ All you need to do is edit the [themes/index.js](./themes/index.js) file and add
|
|||||||
|
|
||||||
While creating the Pull request to add a new theme **don't forget to add a screenshot of how your theme looks**, you can also test how it looks using custom URL parameters like `title_color`, `icon_color`, `bg_color`, `text_color`, `border_color`
|
While creating the Pull request to add a new theme **don't forget to add a screenshot of how your theme looks**, you can also test how it looks using custom URL parameters like `title_color`, `icon_color`, `bg_color`, `text_color`, `border_color`
|
||||||
|
|
||||||
> **Note**
|
> NOTE: If you are contributing your theme just because you are using it personally, then you can [customize the looks](./readme.md#customization) of your card with URL params instead.
|
||||||
> If you are contributing your theme just because you are using it personally, then you can [customize the looks](./readme.md#customization) of your card with URL params instead.
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> If your theme name contains multiple words (ie, hyphenated or underscor-ed theme names), please add two versions of theme: one with hyphens and one with underscores. This is to ensure backward-compatability with when we had hyphens for some themes and underscores for others.
|
|
||||||
|
|
||||||
## Any contributions you make will be under the MIT Software License
|
## Any contributions you make will be under the MIT Software License
|
||||||
|
|
||||||
|
|||||||
+25
-3
@@ -7,6 +7,7 @@ import {
|
|||||||
parseBoolean,
|
parseBoolean,
|
||||||
renderError,
|
renderError,
|
||||||
} from "../src/common/utils.js";
|
} from "../src/common/utils.js";
|
||||||
|
import { HttpException } from "../src/common/exceptions.js";
|
||||||
import { fetchStats } from "../src/fetchers/stats-fetcher.js";
|
import { fetchStats } from "../src/fetchers/stats-fetcher.js";
|
||||||
import { isLocaleAvailable } from "../src/translations.js";
|
import { isLocaleAvailable } from "../src/translations.js";
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ export default async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const cacheSeconds = clampValue(
|
const cacheSeconds = clampValue(
|
||||||
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
|
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
|
||||||
CONSTANTS.FOUR_HOURS,
|
CONSTANTS.FOUR_HOURS,
|
||||||
CONSTANTS.ONE_DAY,
|
CONSTANTS.ONE_DAY,
|
||||||
);
|
);
|
||||||
@@ -65,7 +66,8 @@ export default async (req, res) => {
|
|||||||
"Cache-Control",
|
"Cache-Control",
|
||||||
`max-age=${
|
`max-age=${
|
||||||
cacheSeconds / 2
|
cacheSeconds / 2
|
||||||
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY},
|
||||||
|
stale-if-error=${CONSTANTS.ONE_HOUR}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.send(
|
return res.send(
|
||||||
@@ -93,7 +95,27 @@ export default async (req, res) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
// Throw error if REST and GraphQL API calls fail. This way we can return a cached
|
||||||
|
if (err instanceof HttpException) {
|
||||||
|
// throw err;
|
||||||
|
// throw new Error(err.message);
|
||||||
|
// return res.status(404).;
|
||||||
|
// return {statusCode: 404, body: err.message}
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.send(
|
||||||
|
renderError(err.errors[0].message, err.errors[0].secondaryMessage),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the error response less frequently.
|
||||||
|
res.setHeader(
|
||||||
|
"Cache-Control",
|
||||||
|
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
|
||||||
|
CONSTANTS.ERROR_CACHE_SECONDS
|
||||||
|
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}
|
||||||
|
stale-if-error=${CONSTANTS.ONE_HOUR}`,
|
||||||
|
);
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+7
-2
@@ -40,7 +40,7 @@ export default async (req, res) => {
|
|||||||
const repoData = await fetchRepo(username, repo);
|
const repoData = await fetchRepo(username, repo);
|
||||||
|
|
||||||
let cacheSeconds = clampValue(
|
let cacheSeconds = clampValue(
|
||||||
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
|
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
|
||||||
CONSTANTS.FOUR_HOURS,
|
CONSTANTS.FOUR_HOURS,
|
||||||
CONSTANTS.ONE_DAY,
|
CONSTANTS.ONE_DAY,
|
||||||
);
|
);
|
||||||
@@ -80,7 +80,12 @@ export default async (req, res) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
res.setHeader(
|
||||||
|
"Cache-Control",
|
||||||
|
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
|
||||||
|
CONSTANTS.ERROR_CACHE_SECONDS
|
||||||
|
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
||||||
|
); // Cache the error response less frequently.
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 10 minutes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { logger, request, dateDiff } from "../../src/common/utils.js";
|
|
||||||
export const RATE_LIMIT_SECONDS = 60 * 5; // 1 request per 10 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 10 minutes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import retryer from "../../src/common/retryer.js";
|
|
||||||
import { logger, request } from "../../src/common/utils.js";
|
|
||||||
|
|
||||||
export const RATE_LIMIT_SECONDS = 60 * 10; // 1 request per 10 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
+7
-4
@@ -30,7 +30,6 @@ export default async (req, res) => {
|
|||||||
border_radius,
|
border_radius,
|
||||||
border_color,
|
border_color,
|
||||||
disable_animations,
|
disable_animations,
|
||||||
hide_progress,
|
|
||||||
} = req.query;
|
} = req.query;
|
||||||
res.setHeader("Content-Type", "image/svg+xml");
|
res.setHeader("Content-Type", "image/svg+xml");
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ export default async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const cacheSeconds = clampValue(
|
const cacheSeconds = clampValue(
|
||||||
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
|
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
|
||||||
CONSTANTS.FOUR_HOURS,
|
CONSTANTS.FOUR_HOURS,
|
||||||
CONSTANTS.ONE_DAY,
|
CONSTANTS.ONE_DAY,
|
||||||
);
|
);
|
||||||
@@ -78,11 +77,15 @@ export default async (req, res) => {
|
|||||||
border_color,
|
border_color,
|
||||||
locale: locale ? locale.toLowerCase() : null,
|
locale: locale ? locale.toLowerCase() : null,
|
||||||
disable_animations: parseBoolean(disable_animations),
|
disable_animations: parseBoolean(disable_animations),
|
||||||
hide_progress: parseBoolean(hide_progress),
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
res.setHeader(
|
||||||
|
"Cache-Control",
|
||||||
|
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
|
||||||
|
CONSTANTS.ERROR_CACHE_SECONDS
|
||||||
|
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
||||||
|
); // Cache the error response less frequently.
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+7
-6
@@ -43,15 +43,11 @@ export default async (req, res) => {
|
|||||||
const stats = await fetchWakatimeStats({ username, api_domain, range });
|
const stats = await fetchWakatimeStats({ username, api_domain, range });
|
||||||
|
|
||||||
let cacheSeconds = clampValue(
|
let cacheSeconds = clampValue(
|
||||||
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
|
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
|
||||||
CONSTANTS.FOUR_HOURS,
|
CONSTANTS.FOUR_HOURS,
|
||||||
CONSTANTS.ONE_DAY,
|
CONSTANTS.ONE_DAY,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!cache_seconds) {
|
|
||||||
cacheSeconds = CONSTANTS.FOUR_HOURS;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.setHeader(
|
res.setHeader(
|
||||||
"Cache-Control",
|
"Cache-Control",
|
||||||
`max-age=${
|
`max-age=${
|
||||||
@@ -80,7 +76,12 @@ export default async (req, res) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
|
res.setHeader(
|
||||||
|
"Cache-Control",
|
||||||
|
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
|
||||||
|
CONSTANTS.ERROR_CACHE_SECONDS
|
||||||
|
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
||||||
|
); // Cache the error response less frequently.
|
||||||
return res.send(renderError(err.message, err.secondaryMessage));
|
return res.send(renderError(err.message, err.secondaryMessage));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
import statsCard from "./api/index.js";
|
|
||||||
import repoCard from "./api/pin.js";
|
|
||||||
import langCard from "./api/top-langs.js";
|
|
||||||
import wakatimeCard from "./api/wakatime.js";
|
|
||||||
import express from "express";
|
|
||||||
import dotenv from "dotenv";
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
const app = express();
|
|
||||||
app.listen(process.env.port || 9000);
|
|
||||||
|
|
||||||
app.get("/", statsCard);
|
|
||||||
app.get("/pin", repoCard);
|
|
||||||
app.get("/top-langs", langCard);
|
|
||||||
app.get("/wakatime", wakatimeCard);
|
|
||||||
Generated
+1
-1
@@ -22,7 +22,7 @@
|
|||||||
"@testing-library/dom": "^8.17.1",
|
"@testing-library/dom": "^8.17.1",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@uppercod/css-to-object": "^1.1.1",
|
"@uppercod/css-to-object": "^1.1.1",
|
||||||
"axios-mock-adapter": "^1.21.2",
|
"axios-mock-adapter": "^1.18.1",
|
||||||
"color-contrast-checker": "^2.1.0",
|
"color-contrast-checker": "^2.1.0",
|
||||||
"hjson": "^3.2.2",
|
"hjson": "^3.2.2",
|
||||||
"husky": "^8.0.0",
|
"husky": "^8.0.0",
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@
|
|||||||
"@testing-library/dom": "^8.17.1",
|
"@testing-library/dom": "^8.17.1",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@uppercod/css-to-object": "^1.1.1",
|
"@uppercod/css-to-object": "^1.1.1",
|
||||||
"axios-mock-adapter": "^1.21.2",
|
"axios-mock-adapter": "^1.18.1",
|
||||||
"color-contrast-checker": "^2.1.0",
|
"color-contrast-checker": "^2.1.0",
|
||||||
"hjson": "^3.2.2",
|
"hjson": "^3.2.2",
|
||||||
"husky": "^8.0.0",
|
"husky": "^8.0.0",
|
||||||
|
|||||||
@@ -92,9 +92,7 @@ Visit <https://indiafightscorona.giveindia.org> and make a small donation to hel
|
|||||||
- [Repo Card Exclusive Options](#repo-card-exclusive-options)
|
- [Repo Card Exclusive Options](#repo-card-exclusive-options)
|
||||||
- [Language Card Exclusive Options](#language-card-exclusive-options)
|
- [Language Card Exclusive Options](#language-card-exclusive-options)
|
||||||
- [Wakatime Card Exclusive Option](#wakatime-card-exclusive-options)
|
- [Wakatime Card Exclusive Option](#wakatime-card-exclusive-options)
|
||||||
- [Deploy Yourself](#deploy-on-your-own)
|
- [Deploy Yourself](#deploy-on-your-own-vercel-instance)
|
||||||
- [On Vercel](#on-vercel)
|
|
||||||
- [On other platforms](#on-other-platforms)
|
|
||||||
- [Keep your fork up to date](#keep-your-fork-up-to-date)
|
- [Keep your fork up to date](#keep-your-fork-up-to-date)
|
||||||
|
|
||||||
# GitHub Stats Card
|
# GitHub Stats Card
|
||||||
@@ -135,7 +133,7 @@ You can add the count of all your private contributions to the total commits cou
|
|||||||
|
|
||||||
### Showing icons
|
### Showing icons
|
||||||
|
|
||||||
To enable icons, you can pass `&show_icons=true` in the query param, like so:
|
To enable icons, you can pass `show_icons=true` in the query param, like so:
|
||||||
|
|
||||||
```md
|
```md
|
||||||

|

|
||||||
@@ -307,7 +305,6 @@ You can provide multiple comma-separated values in the bg_color option to render
|
|||||||
- `exclude_repo` - Exclude specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
|
- `exclude_repo` - Exclude specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
|
||||||
- `custom_title` - Sets a custom title for the card _(string)_. Default `Most Used Languages`.
|
- `custom_title` - Sets a custom title for the card _(string)_. Default `Most Used Languages`.
|
||||||
- `disable_animations` - Disables all animations in the card _(boolean)_. Default: `false`.
|
- `disable_animations` - Disables all animations in the card _(boolean)_. Default: `false`.
|
||||||
- `hide_progress` - It uses the compact layout option, hides percentages, and removes the bars. Default: `false`.
|
|
||||||
|
|
||||||
> **Warning**
|
> **Warning**
|
||||||
> Language names should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding)
|
> Language names should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding)
|
||||||
@@ -401,14 +398,6 @@ You can use the `&layout=compact` option to change the card design.
|
|||||||
[](https://github.com/anuraghazra/github-readme-stats)
|
[](https://github.com/anuraghazra/github-readme-stats)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Hide Progress Bars
|
|
||||||
|
|
||||||
You can use the `&hide_progress=true` option to hide the percentages and the progress bars (layout will be automatically set to `compact`).
|
|
||||||
|
|
||||||
```md
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Demo
|
### Demo
|
||||||
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats)
|
[](https://github.com/anuraghazra/github-readme-stats)
|
||||||
@@ -417,10 +406,6 @@ You can use the `&hide_progress=true` option to hide the percentages and the pro
|
|||||||
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats)
|
[](https://github.com/anuraghazra/github-readme-stats)
|
||||||
|
|
||||||
- Hidden progress bars
|
|
||||||
|
|
||||||
[](https://github.com/anuraghazra/github-readme-stats)
|
|
||||||
|
|
||||||
# Wakatime Week Stats
|
# Wakatime Week Stats
|
||||||
|
|
||||||
Change the `?username=` value to your [Wakatime](https://wakatime.com) username.
|
Change the `?username=` value to your [Wakatime](https://wakatime.com) username.
|
||||||
@@ -511,9 +496,7 @@ By default, GitHub does not lay out the cards side by side. To do that, you can
|
|||||||
</a>
|
</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploy on your own
|
## Deploy on your own Vercel instance
|
||||||
|
|
||||||
### On Vercel
|
|
||||||
|
|
||||||
#### :film_projector: [Check Out Step By Step Video Tutorial By @codeSTACKr](https://youtu.be/n6d4KHSKqGk?t=107)
|
#### :film_projector: [Check Out Step By Step Video Tutorial By @codeSTACKr](https://youtu.be/n6d4KHSKqGk?t=107)
|
||||||
|
|
||||||
@@ -550,23 +533,6 @@ Since the GitHub API only allows 5k requests per hour, my `https://github-readme
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### On other platforms
|
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
> This way of using GRS is not officially supported and was added to cater to some particular use cases where Vercel could not be used (e.g. #2341). The support for this method, therefore, is limited.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>:hammer_and_wrench: Step-by-step guide for deploying on other platforms</b></summary>
|
|
||||||
|
|
||||||
1. Fork or clone this repo as per your needs
|
|
||||||
2. Add `express` to the dependencies section of `package.json`
|
|
||||||
https://github.com/anuraghazra/github-readme-stats/blob/ba7c2f8b55eac8452e479c8bd38b044d204d0424/package.json#L54-L61
|
|
||||||
3. Run `npm i` if needed (initial setup)
|
|
||||||
4. Run `node express.js` to start the server, or set the entry point to `express.js` in `package.json` if you're deploying on a managed service
|
|
||||||
https://github.com/anuraghazra/github-readme-stats/blob/ba7c2f8b55eac8452e479c8bd38b044d204d0424/package.json#L11
|
|
||||||
5. You're done 🎉
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Keep your fork up to date
|
### Keep your fork up to date
|
||||||
|
|
||||||
You can keep your fork, and thus your private Vercel instance up to date with the upstream using GitHubs' [Sync Fork button](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork). You can also use the [pull](https://github.com/wei/pull) package created by [@wei](https://github.com/wei) to automate this process.
|
You can keep your fork, and thus your private Vercel instance up to date with the upstream using GitHubs' [Sync Fork button](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork). You can also use the [pull](https://github.com/wei/pull) package created by [@wei](https://github.com/wei) to automate this process.
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ const createStatMdLink = (theme) => {
|
|||||||
const generateLinks = (fn) => {
|
const generateLinks = (fn) => {
|
||||||
return Object.keys(themes)
|
return Object.keys(themes)
|
||||||
.map((name) => fn(name))
|
.map((name) => fn(name))
|
||||||
.filter((name) => !name.includes('-'))
|
|
||||||
.join("");
|
.join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,7 +67,7 @@ const generateTable = ({ isRepoCard }) => {
|
|||||||
const rows = [];
|
const rows = [];
|
||||||
const themesFiltered = Object.keys(themes).filter(
|
const themesFiltered = Object.keys(themes).filter(
|
||||||
(name) => name !== (!isRepoCard ? "default_repocard" : "default"),
|
(name) => name !== (!isRepoCard ? "default_repocard" : "default"),
|
||||||
).filter((name) => !name.includes('-'));
|
);
|
||||||
|
|
||||||
for (let i = 0; i < themesFiltered.length; i += 3) {
|
for (let i = 0; i < themesFiltered.length; i += 3) {
|
||||||
const one = themesFiltered[i];
|
const one = themesFiltered[i];
|
||||||
|
|||||||
@@ -298,13 +298,12 @@ const themeNameAlreadyExists = (name) => {
|
|||||||
return themes[name] !== undefined;
|
return themes[name] !== undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DRY_RUN = process.env.DRY_RUN === "true" || false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main function.
|
* Main function.
|
||||||
*/
|
*/
|
||||||
export const run = async (prNumber) => {
|
export const run = async (prNumber) => {
|
||||||
try {
|
try {
|
||||||
|
const dryRun = process.env.DRY_RUN === "true" || false;
|
||||||
debug("Retrieve action information from context...");
|
debug("Retrieve action information from context...");
|
||||||
debug(`Context: ${inspect(github.context)}`);
|
debug(`Context: ${inspect(github.context)}`);
|
||||||
let commentBody = `
|
let commentBody = `
|
||||||
@@ -514,7 +513,7 @@ export const run = async (prNumber) => {
|
|||||||
// Create or update theme-preview comment.
|
// Create or update theme-preview comment.
|
||||||
debug("Create or update theme-preview comment...");
|
debug("Create or update theme-preview comment...");
|
||||||
let comment_url;
|
let comment_url;
|
||||||
if (!DRY_RUN) {
|
if (!dryRun) {
|
||||||
comment_url = await upsertComment(octokit, {
|
comment_url = await upsertComment(octokit, {
|
||||||
comment_id: comment?.id,
|
comment_id: comment?.id,
|
||||||
issue_number: pullRequestId,
|
issue_number: pullRequestId,
|
||||||
@@ -536,7 +535,7 @@ export const run = async (prNumber) => {
|
|||||||
const reviewReason = themesValid
|
const reviewReason = themesValid
|
||||||
? undefined
|
? undefined
|
||||||
: INVALID_REVIEW_COMMENT(comment_url);
|
: INVALID_REVIEW_COMMENT(comment_url);
|
||||||
if (!DRY_RUN) {
|
if (!dryRun) {
|
||||||
await addReview(
|
await addReview(
|
||||||
octokit,
|
octokit,
|
||||||
pullRequestId,
|
pullRequestId,
|
||||||
@@ -559,7 +558,7 @@ export const run = async (prNumber) => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debug("Set review state to `REQUEST_CHANGES` and add `invalid` label...");
|
debug("Set review state to `REQUEST_CHANGES` and add `invalid` label...");
|
||||||
if (!DRY_RUN) {
|
if (!dryRun) {
|
||||||
await addReview(
|
await addReview(
|
||||||
octokit,
|
octokit,
|
||||||
pullRequestId,
|
pullRequestId,
|
||||||
|
|||||||
+11
-25
@@ -12,11 +12,6 @@ import {
|
|||||||
import { getStyles } from "../getStyles.js";
|
import { getStyles } from "../getStyles.js";
|
||||||
import { statCardLocales } from "../translations.js";
|
import { statCardLocales } from "../translations.js";
|
||||||
|
|
||||||
const CARD_MIN_WIDTH = 287;
|
|
||||||
const CARD_DEFAULT_WIDTH = 287;
|
|
||||||
const RANK_CARD_MIN_WIDTH = 420;
|
|
||||||
const RANK_CARD_DEFAULT_WIDTH = 450;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a stats card text item.
|
* Create a stats card text item.
|
||||||
*
|
*
|
||||||
@@ -223,17 +218,11 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
When hide_rank=false, the minimum card_width is 340 px + the icon width (if show_icons=true).
|
When hide_rank=false, the minimum card_width is 340 px + the icon width (if show_icons=true).
|
||||||
Numbers are picked by looking at existing dimensions on production.
|
Numbers are picked by looking at existing dimensions on production.
|
||||||
*/
|
*/
|
||||||
const iconWidth = show_icons ? 16 + /* padding */ 1 : 0;
|
const iconWidth = show_icons ? 16 : 0;
|
||||||
const minCardWidth =
|
const minCardWidth = hide_rank
|
||||||
(hide_rank
|
? clampValue(50 /* padding */ + calculateTextWidth() * 2, 270, Infinity)
|
||||||
? clampValue(
|
: 340 + iconWidth;
|
||||||
50 /* padding */ + calculateTextWidth() * 2,
|
const defaultCardWidth = hide_rank ? 270 : 495;
|
||||||
CARD_MIN_WIDTH,
|
|
||||||
Infinity,
|
|
||||||
)
|
|
||||||
: RANK_CARD_MIN_WIDTH) + iconWidth;
|
|
||||||
const defaultCardWidth =
|
|
||||||
(hide_rank ? CARD_DEFAULT_WIDTH : RANK_CARD_DEFAULT_WIDTH) + iconWidth;
|
|
||||||
let width = isNaN(card_width) ? defaultCardWidth : card_width;
|
let width = isNaN(card_width) ? defaultCardWidth : card_width;
|
||||||
if (width < minCardWidth) {
|
if (width < minCardWidth) {
|
||||||
width = minCardWidth;
|
width = minCardWidth;
|
||||||
@@ -262,21 +251,18 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the right rank circle translation values such that the rank circle
|
* Calculates the right rank circle translation values such that the rank circle
|
||||||
* keeps respecting the following padding:
|
* keeps respecting the padding.
|
||||||
*
|
*
|
||||||
* width > RANK_CARD_DEFAULT_WIDTH: The default right padding of 70 px will be used.
|
* width > 450: The default left padding of 50 px will be used.
|
||||||
* width < RANK_CARD_DEFAULT_WIDTH: The left and right padding will be enlarged
|
* width < 450: The left and right padding will shrink equally.
|
||||||
* equally from a certain minimum at RANK_CARD_MIN_WIDTH.
|
|
||||||
*
|
*
|
||||||
* @returns {number} - Rank circle translation value.
|
* @returns {number} - Rank circle translation value.
|
||||||
*/
|
*/
|
||||||
const calculateRankXTranslation = () => {
|
const calculateRankXTranslation = () => {
|
||||||
const minXTranslation = RANK_CARD_MIN_WIDTH + iconWidth - 70;
|
if (width < 450) {
|
||||||
if (width > RANK_CARD_DEFAULT_WIDTH) {
|
return width - 95 + (45 * (450 - 340)) / 110;
|
||||||
const xMaxExpansion = minXTranslation + (450 - minCardWidth) / 2;
|
|
||||||
return xMaxExpansion + width - RANK_CARD_DEFAULT_WIDTH;
|
|
||||||
} else {
|
} else {
|
||||||
return minXTranslation + (width - minCardWidth) / 2;
|
return width - 95;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { langCardLocales } from "../translations.js";
|
import { langCardLocales } from "../translations.js";
|
||||||
|
|
||||||
const DEFAULT_CARD_WIDTH = 300;
|
const DEFAULT_CARD_WIDTH = 300;
|
||||||
const MIN_CARD_WIDTH = 280;
|
const MIN_CARD_WIDTH = 230;
|
||||||
const DEFAULT_LANGS_COUNT = 5;
|
const DEFAULT_LANGS_COUNT = 5;
|
||||||
const DEFAULT_LANG_COLOR = "#858585";
|
const DEFAULT_LANG_COLOR = "#858585";
|
||||||
const CARD_PADDING = 25;
|
const CARD_PADDING = 25;
|
||||||
@@ -76,11 +76,10 @@ const createProgressTextNode = ({ width, color, name, progress, index }) => {
|
|||||||
* @param {object} props Function properties.
|
* @param {object} props Function properties.
|
||||||
* @param {Lang} props.lang Programming language object.
|
* @param {Lang} props.lang Programming language object.
|
||||||
* @param {number} props.totalSize Total size of all languages.
|
* @param {number} props.totalSize Total size of all languages.
|
||||||
* @param {boolean} props.hideProgress Whether to hide percentage.
|
|
||||||
* @param {number} props.index Index of the programming language.
|
* @param {number} props.index Index of the programming language.
|
||||||
* @returns {string} Compact layout programming language SVG node.
|
* @returns {string} Compact layout programming language SVG node.
|
||||||
*/
|
*/
|
||||||
const createCompactLangNode = ({ lang, totalSize, hideProgress, index }) => {
|
const createCompactLangNode = ({ lang, totalSize, index }) => {
|
||||||
const percentage = ((lang.size / totalSize) * 100).toFixed(2);
|
const percentage = ((lang.size / totalSize) * 100).toFixed(2);
|
||||||
const staggerDelay = (index + 3) * 150;
|
const staggerDelay = (index + 3) * 150;
|
||||||
const color = lang.color || "#858585";
|
const color = lang.color || "#858585";
|
||||||
@@ -89,7 +88,7 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, index }) => {
|
|||||||
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
||||||
<circle cx="5" cy="6" r="5" fill="${color}" />
|
<circle cx="5" cy="6" r="5" fill="${color}" />
|
||||||
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
||||||
${lang.name} ${hideProgress ? "" : percentage + "%"}
|
${lang.name} ${percentage}%
|
||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
`;
|
`;
|
||||||
@@ -101,10 +100,9 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, index }) => {
|
|||||||
* @param {object[]} props Function properties.
|
* @param {object[]} props Function properties.
|
||||||
* @param {Lang[]} props.langs Array of programming languages.
|
* @param {Lang[]} props.langs Array of programming languages.
|
||||||
* @param {number} props.totalSize Total size of all languages.
|
* @param {number} props.totalSize Total size of all languages.
|
||||||
* @param {boolean} props.hideProgress Whether to hide percentage.
|
|
||||||
* @returns {string} Programming languages SVG node.
|
* @returns {string} Programming languages SVG node.
|
||||||
*/
|
*/
|
||||||
const createLanguageTextNode = ({ langs, totalSize, hideProgress }) => {
|
const createLanguageTextNode = ({ langs, totalSize }) => {
|
||||||
const longestLang = getLongestLang(langs);
|
const longestLang = getLongestLang(langs);
|
||||||
const chunked = chunkArray(langs, langs.length / 2);
|
const chunked = chunkArray(langs, langs.length / 2);
|
||||||
const layouts = chunked.map((array) => {
|
const layouts = chunked.map((array) => {
|
||||||
@@ -113,7 +111,6 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress }) => {
|
|||||||
createCompactLangNode({
|
createCompactLangNode({
|
||||||
lang,
|
lang,
|
||||||
totalSize,
|
totalSize,
|
||||||
hideProgress,
|
|
||||||
index,
|
index,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -163,10 +160,9 @@ const renderNormalLayout = (langs, width, totalLanguageSize) => {
|
|||||||
* @param {Lang[]} langs Array of programming languages.
|
* @param {Lang[]} langs Array of programming languages.
|
||||||
* @param {number} width Card width.
|
* @param {number} width Card width.
|
||||||
* @param {number} totalLanguageSize Total size of all languages.
|
* @param {number} totalLanguageSize Total size of all languages.
|
||||||
* @param {boolean} hideProgress Whether to hide progress bar.
|
|
||||||
* @returns {string} Compact layout card SVG object.
|
* @returns {string} Compact layout card SVG object.
|
||||||
*/
|
*/
|
||||||
const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress) => {
|
const renderCompactLayout = (langs, width, totalLanguageSize) => {
|
||||||
const paddingRight = 50;
|
const paddingRight = 50;
|
||||||
const offsetWidth = width - paddingRight;
|
const offsetWidth = width - paddingRight;
|
||||||
// progressOffset holds the previous language's width and used to offset the next language
|
// progressOffset holds the previous language's width and used to offset the next language
|
||||||
@@ -197,21 +193,15 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress) => {
|
|||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
return `
|
return `
|
||||||
${
|
<mask id="rect-mask">
|
||||||
!hideProgress
|
|
||||||
? `
|
|
||||||
<mask id="rect-mask">
|
|
||||||
<rect x="0" y="0" width="${offsetWidth}" height="8" fill="white" rx="5"/>
|
<rect x="0" y="0" width="${offsetWidth}" height="8" fill="white" rx="5"/>
|
||||||
</mask>
|
</mask>
|
||||||
${compactProgressBar}
|
${compactProgressBar}
|
||||||
`
|
|
||||||
: ""
|
<g transform="translate(0, 25)">
|
||||||
}
|
|
||||||
<g transform="translate(0, ${hideProgress ? "0" : "25"})">
|
|
||||||
${createLanguageTextNode({
|
${createLanguageTextNode({
|
||||||
langs,
|
langs,
|
||||||
totalSize: totalLanguageSize,
|
totalSize: totalLanguageSize,
|
||||||
hideProgress: hideProgress,
|
|
||||||
})}
|
})}
|
||||||
</g>
|
</g>
|
||||||
`;
|
`;
|
||||||
@@ -286,7 +276,6 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
|||||||
text_color,
|
text_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
hide,
|
hide,
|
||||||
hide_progress,
|
|
||||||
theme,
|
theme,
|
||||||
layout,
|
layout,
|
||||||
custom_title,
|
custom_title,
|
||||||
@@ -316,16 +305,11 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
|||||||
let height = calculateNormalLayoutHeight(langs.length);
|
let height = calculateNormalLayoutHeight(langs.length);
|
||||||
|
|
||||||
let finalLayout = "";
|
let finalLayout = "";
|
||||||
if (layout === "compact" || hide_progress == true) {
|
if (layout === "compact") {
|
||||||
height =
|
width = width + 50; // padding
|
||||||
calculateCompactLayoutHeight(langs.length) + (hide_progress ? -25 : 0);
|
height = calculateCompactLayoutHeight(langs.length);
|
||||||
|
|
||||||
finalLayout = renderCompactLayout(
|
finalLayout = renderCompactLayout(langs, width, totalLanguageSize);
|
||||||
langs,
|
|
||||||
width,
|
|
||||||
totalLanguageSize,
|
|
||||||
hide_progress,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
finalLayout = renderNormalLayout(langs, width, totalLanguageSize);
|
finalLayout = renderNormalLayout(langs, width, totalLanguageSize);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-1
@@ -38,7 +38,6 @@ export type TopLangOptions = CommonOptions & {
|
|||||||
custom_title: string;
|
custom_title: string;
|
||||||
langs_count: number;
|
langs_count: number;
|
||||||
disable_animations: boolean;
|
disable_animations: boolean;
|
||||||
hide_progress: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type WakaTimeOptions = CommonOptions & {
|
type WakaTimeOptions = CommonOptions & {
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ const createTextNode = ({
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
name: label,
|
name: label,
|
||||||
progressBarBackgroundColor,
|
progressBarBackgroundColor,
|
||||||
delay: staggerDelay + 300,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@@ -277,12 +276,11 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
} else {
|
} else {
|
||||||
finalLayout = flexLayout({
|
finalLayout = flexLayout({
|
||||||
items: filteredLanguages.length
|
items: filteredLanguages.length
|
||||||
? filteredLanguages.map((language, index) => {
|
? filteredLanguages.map((language) => {
|
||||||
return createTextNode({
|
return createTextNode({
|
||||||
id: language.name,
|
id: language.name,
|
||||||
label: language.name,
|
label: language.name,
|
||||||
value: language.text,
|
value: language.text,
|
||||||
index: index,
|
|
||||||
percent: language.percent,
|
percent: language.percent,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
progressBarColor: titleColor,
|
progressBarColor: titleColor,
|
||||||
@@ -323,29 +321,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
card.setCSS(
|
card.setCSS(
|
||||||
`
|
`
|
||||||
${cssStyles}
|
${cssStyles}
|
||||||
@keyframes slideInAnimation {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: calc(100%-100px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes growWidthAnimation {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
|
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
|
||||||
#rect-mask rect{
|
|
||||||
animation: slideInAnimation 1s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
.lang-progress{
|
|
||||||
animation: growWidthAnimation 0.6s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* @file GRS Exception/Errors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SECONDARY_ERROR_MESSAGES = {
|
||||||
|
MAX_RETRY:
|
||||||
|
"Please add an env variable called PAT_1 with your github token in vercel",
|
||||||
|
USER_NOT_FOUND: "Make sure the provided username is not an organization",
|
||||||
|
GRAPHQL_ERROR: "Please try again later",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom error class to handle custom GRS errors.
|
||||||
|
*
|
||||||
|
* @extends Error
|
||||||
|
*/
|
||||||
|
class CustomError extends Error {
|
||||||
|
/**
|
||||||
|
* @param {string} message Error message.
|
||||||
|
* @param {string} type Error type.
|
||||||
|
*/
|
||||||
|
constructor(message, type) {
|
||||||
|
super(message);
|
||||||
|
this.type = type;
|
||||||
|
this.secondaryMessage = SECONDARY_ERROR_MESSAGES[type] || type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MAX_RETRY = "MAX_RETRY";
|
||||||
|
static USER_NOT_FOUND = "USER_NOT_FOUND";
|
||||||
|
static GRAPHQL_ERROR = "GRAPHQL_ERROR";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Missing query parameter class.
|
||||||
|
*
|
||||||
|
* @extends Error
|
||||||
|
*/
|
||||||
|
class MissingParamError extends Error {
|
||||||
|
/**
|
||||||
|
* @param {string[]} missedParams
|
||||||
|
* @param {string?=} secondaryMessage
|
||||||
|
*/
|
||||||
|
constructor(missedParams, secondaryMessage) {
|
||||||
|
const msg = `Missing params ${missedParams
|
||||||
|
.map((p) => `"${p}"`)
|
||||||
|
.join(", ")} make sure you pass the parameters in URL`;
|
||||||
|
super(msg);
|
||||||
|
this.missedParams = missedParams;
|
||||||
|
this.secondaryMessage = secondaryMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpException class.
|
||||||
|
*
|
||||||
|
* @extends Error
|
||||||
|
*/
|
||||||
|
class HttpException extends Error {
|
||||||
|
/**
|
||||||
|
* Create a HttpException.
|
||||||
|
*
|
||||||
|
* @param {number} statusCode - The status code.
|
||||||
|
* @param {string} message - The error message.
|
||||||
|
* @param {string[]} errors - The errors.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
statusCode,
|
||||||
|
message = "Exception occurred during the processing of HTTP requests.",
|
||||||
|
errors = [],
|
||||||
|
) {
|
||||||
|
super(message);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
SECONDARY_ERROR_MESSAGES,
|
||||||
|
CustomError,
|
||||||
|
MissingParamError,
|
||||||
|
HttpException,
|
||||||
|
};
|
||||||
+1
-2
@@ -21,10 +21,9 @@ export {
|
|||||||
wrapTextMultiline,
|
wrapTextMultiline,
|
||||||
logger,
|
logger,
|
||||||
CONSTANTS,
|
CONSTANTS,
|
||||||
CustomError,
|
|
||||||
MissingParamError,
|
|
||||||
measureText,
|
measureText,
|
||||||
lowercaseTrim,
|
lowercaseTrim,
|
||||||
chunkArray,
|
chunkArray,
|
||||||
parseEmojis,
|
parseEmojis,
|
||||||
} from "./utils.js";
|
} from "./utils.js";
|
||||||
|
export { CustomError, MissingParamError, HttpException } from "./exceptions.js";
|
||||||
|
|||||||
@@ -79,7 +79,6 @@
|
|||||||
"Ceylon": "#dfa535",
|
"Ceylon": "#dfa535",
|
||||||
"Chapel": "#8dc63f",
|
"Chapel": "#8dc63f",
|
||||||
"ChucK": "#3f8000",
|
"ChucK": "#3f8000",
|
||||||
"Circom": "#707575",
|
|
||||||
"Cirru": "#ccccff",
|
"Cirru": "#ccccff",
|
||||||
"Clarion": "#db901e",
|
"Clarion": "#db901e",
|
||||||
"Clarity": "#5546ff",
|
"Clarity": "#5546ff",
|
||||||
@@ -103,7 +102,6 @@
|
|||||||
"Csound Score": "#1a1a1a",
|
"Csound Score": "#1a1a1a",
|
||||||
"Cuda": "#3A4E3A",
|
"Cuda": "#3A4E3A",
|
||||||
"Curry": "#531242",
|
"Curry": "#531242",
|
||||||
"Cypher": "#34c0eb",
|
|
||||||
"Cython": "#fedf5b",
|
"Cython": "#fedf5b",
|
||||||
"D": "#ba595e",
|
"D": "#ba595e",
|
||||||
"DM": "#447265",
|
"DM": "#447265",
|
||||||
@@ -117,7 +115,6 @@
|
|||||||
"DirectX 3D File": "#aace60",
|
"DirectX 3D File": "#aace60",
|
||||||
"Dockerfile": "#384d54",
|
"Dockerfile": "#384d54",
|
||||||
"Dogescript": "#cca760",
|
"Dogescript": "#cca760",
|
||||||
"Dotenv": "#e5d559",
|
|
||||||
"Dylan": "#6c616e",
|
"Dylan": "#6c616e",
|
||||||
"E": "#ccce35",
|
"E": "#ccce35",
|
||||||
"ECL": "#8a1267",
|
"ECL": "#8a1267",
|
||||||
@@ -127,7 +124,6 @@
|
|||||||
"Earthly": "#2af0ff",
|
"Earthly": "#2af0ff",
|
||||||
"Easybuild": "#069406",
|
"Easybuild": "#069406",
|
||||||
"Ecere Projects": "#913960",
|
"Ecere Projects": "#913960",
|
||||||
"Ecmarkup": "#eb8131",
|
|
||||||
"EditorConfig": "#fff1f2",
|
"EditorConfig": "#fff1f2",
|
||||||
"Eiffel": "#4d6977",
|
"Eiffel": "#4d6977",
|
||||||
"Elixir": "#6e4a7e",
|
"Elixir": "#6e4a7e",
|
||||||
@@ -151,7 +147,7 @@
|
|||||||
"Forth": "#341708",
|
"Forth": "#341708",
|
||||||
"Fortran": "#4d41b1",
|
"Fortran": "#4d41b1",
|
||||||
"Fortran Free Form": "#4d41b1",
|
"Fortran Free Form": "#4d41b1",
|
||||||
"FreeBasic": "#141AC9",
|
"FreeBasic": "#867db1",
|
||||||
"FreeMarker": "#0050b2",
|
"FreeMarker": "#0050b2",
|
||||||
"Frege": "#00cafe",
|
"Frege": "#00cafe",
|
||||||
"Futhark": "#5f021f",
|
"Futhark": "#5f021f",
|
||||||
@@ -184,7 +180,6 @@
|
|||||||
"Go": "#00ADD8",
|
"Go": "#00ADD8",
|
||||||
"Go Checksums": "#00ADD8",
|
"Go Checksums": "#00ADD8",
|
||||||
"Go Module": "#00ADD8",
|
"Go Module": "#00ADD8",
|
||||||
"Godot Resource": "#355570",
|
|
||||||
"Golo": "#88562A",
|
"Golo": "#88562A",
|
||||||
"Gosu": "#82937f",
|
"Gosu": "#82937f",
|
||||||
"Grace": "#615f8b",
|
"Grace": "#615f8b",
|
||||||
@@ -195,7 +190,6 @@
|
|||||||
"Groovy": "#4298b8",
|
"Groovy": "#4298b8",
|
||||||
"Groovy Server Pages": "#4298b8",
|
"Groovy Server Pages": "#4298b8",
|
||||||
"HAProxy": "#106da9",
|
"HAProxy": "#106da9",
|
||||||
"HCL": "#844FBA",
|
|
||||||
"HLSL": "#aace60",
|
"HLSL": "#aace60",
|
||||||
"HOCON": "#9ff8ee",
|
"HOCON": "#9ff8ee",
|
||||||
"HTML": "#e34c26",
|
"HTML": "#e34c26",
|
||||||
@@ -221,7 +215,6 @@
|
|||||||
"Idris": "#b30000",
|
"Idris": "#b30000",
|
||||||
"Ignore List": "#000000",
|
"Ignore List": "#000000",
|
||||||
"ImageJ Macro": "#99AAFF",
|
"ImageJ Macro": "#99AAFF",
|
||||||
"Imba": "#16cec6",
|
|
||||||
"Inno Setup": "#264b99",
|
"Inno Setup": "#264b99",
|
||||||
"Io": "#a9188d",
|
"Io": "#a9188d",
|
||||||
"Ioke": "#078193",
|
"Ioke": "#078193",
|
||||||
@@ -229,7 +222,6 @@
|
|||||||
"Isabelle ROOT": "#FEFE00",
|
"Isabelle ROOT": "#FEFE00",
|
||||||
"J": "#9EEDFF",
|
"J": "#9EEDFF",
|
||||||
"JAR Manifest": "#b07219",
|
"JAR Manifest": "#b07219",
|
||||||
"JCL": "#d90e09",
|
|
||||||
"JFlex": "#DBCA00",
|
"JFlex": "#DBCA00",
|
||||||
"JSON": "#292929",
|
"JSON": "#292929",
|
||||||
"JSON with Comments": "#292929",
|
"JSON with Comments": "#292929",
|
||||||
@@ -252,11 +244,9 @@
|
|||||||
"Jsonnet": "#0064bd",
|
"Jsonnet": "#0064bd",
|
||||||
"Julia": "#a270ba",
|
"Julia": "#a270ba",
|
||||||
"Jupyter Notebook": "#DA5B0B",
|
"Jupyter Notebook": "#DA5B0B",
|
||||||
"Just": "#384d54",
|
|
||||||
"KRL": "#28430A",
|
"KRL": "#28430A",
|
||||||
"Kaitai Struct": "#773b37",
|
"Kaitai Struct": "#773b37",
|
||||||
"KakouneScript": "#6f8042",
|
"KakouneScript": "#6f8042",
|
||||||
"KerboScript": "#41adf0",
|
|
||||||
"KiCad Layout": "#2f4aab",
|
"KiCad Layout": "#2f4aab",
|
||||||
"KiCad Legacy Layout": "#2f4aab",
|
"KiCad Legacy Layout": "#2f4aab",
|
||||||
"KiCad Schematic": "#2f4aab",
|
"KiCad Schematic": "#2f4aab",
|
||||||
@@ -296,7 +286,6 @@
|
|||||||
"Mathematica": "#dd1100",
|
"Mathematica": "#dd1100",
|
||||||
"Max": "#c4a79c",
|
"Max": "#c4a79c",
|
||||||
"Mercury": "#ff2b2b",
|
"Mercury": "#ff2b2b",
|
||||||
"Mermaid": "#ff3670",
|
|
||||||
"Meson": "#007800",
|
"Meson": "#007800",
|
||||||
"Metal": "#8f14e9",
|
"Metal": "#8f14e9",
|
||||||
"MiniYAML": "#ff1111",
|
"MiniYAML": "#ff1111",
|
||||||
@@ -329,10 +318,6 @@
|
|||||||
"Nu": "#c9df40",
|
"Nu": "#c9df40",
|
||||||
"NumPy": "#9C8AF9",
|
"NumPy": "#9C8AF9",
|
||||||
"Nunjucks": "#3d8137",
|
"Nunjucks": "#3d8137",
|
||||||
"OASv2-json": "#85ea2d",
|
|
||||||
"OASv2-yaml": "#85ea2d",
|
|
||||||
"OASv3-json": "#85ea2d",
|
|
||||||
"OASv3-yaml": "#85ea2d",
|
|
||||||
"OCaml": "#3be133",
|
"OCaml": "#3be133",
|
||||||
"ObjectScript": "#424893",
|
"ObjectScript": "#424893",
|
||||||
"Objective-C": "#438eff",
|
"Objective-C": "#438eff",
|
||||||
@@ -342,18 +327,14 @@
|
|||||||
"Omgrofl": "#cabbff",
|
"Omgrofl": "#cabbff",
|
||||||
"Opal": "#f7ede0",
|
"Opal": "#f7ede0",
|
||||||
"Open Policy Agent": "#7d9199",
|
"Open Policy Agent": "#7d9199",
|
||||||
"OpenAPI Specification v2": "#85ea2d",
|
|
||||||
"OpenAPI Specification v3": "#85ea2d",
|
|
||||||
"OpenCL": "#ed2e2d",
|
"OpenCL": "#ed2e2d",
|
||||||
"OpenEdge ABL": "#5ce600",
|
"OpenEdge ABL": "#5ce600",
|
||||||
"OpenQASM": "#AA70FF",
|
"OpenQASM": "#AA70FF",
|
||||||
"OpenSCAD": "#e5cd45",
|
"OpenSCAD": "#e5cd45",
|
||||||
"Option List": "#476732",
|
|
||||||
"Org": "#77aa99",
|
"Org": "#77aa99",
|
||||||
"Oxygene": "#cdd0e3",
|
"Oxygene": "#cdd0e3",
|
||||||
"Oz": "#fab738",
|
"Oz": "#fab738",
|
||||||
"P4": "#7055b5",
|
"P4": "#7055b5",
|
||||||
"PDDL": "#0d00ff",
|
|
||||||
"PEG.js": "#234d6b",
|
"PEG.js": "#234d6b",
|
||||||
"PHP": "#4F5D95",
|
"PHP": "#4F5D95",
|
||||||
"PLSQL": "#dad8d8",
|
"PLSQL": "#dad8d8",
|
||||||
@@ -369,9 +350,7 @@
|
|||||||
"PicoLisp": "#6067af",
|
"PicoLisp": "#6067af",
|
||||||
"PigLatin": "#fcd7de",
|
"PigLatin": "#fcd7de",
|
||||||
"Pike": "#005390",
|
"Pike": "#005390",
|
||||||
"PlantUML": "#fbbd16",
|
|
||||||
"PogoScript": "#d80074",
|
"PogoScript": "#d80074",
|
||||||
"Polar": "#ae81ff",
|
|
||||||
"Portugol": "#f8bd00",
|
"Portugol": "#f8bd00",
|
||||||
"PostCSS": "#dc3a0c",
|
"PostCSS": "#dc3a0c",
|
||||||
"PostScript": "#da291c",
|
"PostScript": "#da291c",
|
||||||
@@ -387,7 +366,6 @@
|
|||||||
"Puppet": "#302B6D",
|
"Puppet": "#302B6D",
|
||||||
"PureBasic": "#5a6986",
|
"PureBasic": "#5a6986",
|
||||||
"PureScript": "#1D222D",
|
"PureScript": "#1D222D",
|
||||||
"Pyret": "#ee1e10",
|
|
||||||
"Python": "#3572A5",
|
"Python": "#3572A5",
|
||||||
"Python console": "#3572A5",
|
"Python console": "#3572A5",
|
||||||
"Python traceback": "#3572A5",
|
"Python traceback": "#3572A5",
|
||||||
@@ -436,7 +414,6 @@
|
|||||||
"Sass": "#a53b70",
|
"Sass": "#a53b70",
|
||||||
"Scala": "#c22d40",
|
"Scala": "#c22d40",
|
||||||
"Scaml": "#bd181a",
|
"Scaml": "#bd181a",
|
||||||
"Scenic": "#fdc700",
|
|
||||||
"Scheme": "#1e4aec",
|
"Scheme": "#1e4aec",
|
||||||
"Scilab": "#ca0f21",
|
"Scilab": "#ca0f21",
|
||||||
"Self": "#0579aa",
|
"Self": "#0579aa",
|
||||||
@@ -444,7 +421,6 @@
|
|||||||
"Shell": "#89e051",
|
"Shell": "#89e051",
|
||||||
"ShellCheck Config": "#cecfcb",
|
"ShellCheck Config": "#cecfcb",
|
||||||
"Shen": "#120F14",
|
"Shen": "#120F14",
|
||||||
"Simple File Verification": "#C9BFED",
|
|
||||||
"Singularity": "#64E6AD",
|
"Singularity": "#64E6AD",
|
||||||
"Slash": "#007eff",
|
"Slash": "#007eff",
|
||||||
"Slice": "#003fa2",
|
"Slice": "#003fa2",
|
||||||
@@ -452,7 +428,6 @@
|
|||||||
"SmPL": "#c94949",
|
"SmPL": "#c94949",
|
||||||
"Smalltalk": "#596706",
|
"Smalltalk": "#596706",
|
||||||
"Smarty": "#f0c040",
|
"Smarty": "#f0c040",
|
||||||
"Smithy": "#c44536",
|
|
||||||
"Solidity": "#AA6746",
|
"Solidity": "#AA6746",
|
||||||
"SourcePawn": "#f69e1d",
|
"SourcePawn": "#f69e1d",
|
||||||
"Squirrel": "#800000",
|
"Squirrel": "#800000",
|
||||||
@@ -503,7 +478,6 @@
|
|||||||
"Vim Script": "#199f4b",
|
"Vim Script": "#199f4b",
|
||||||
"Vim Snippet": "#199f4b",
|
"Vim Snippet": "#199f4b",
|
||||||
"Visual Basic .NET": "#945db7",
|
"Visual Basic .NET": "#945db7",
|
||||||
"Visual Basic 6.0": "#2c6353",
|
|
||||||
"Volt": "#1F1F1F",
|
"Volt": "#1F1F1F",
|
||||||
"Vue": "#41b883",
|
"Vue": "#41b883",
|
||||||
"Vyper": "#2980b9",
|
"Vyper": "#2980b9",
|
||||||
|
|||||||
+13
-4
@@ -1,4 +1,8 @@
|
|||||||
import { CustomError, logger } from "./utils.js";
|
import { logger } from "./utils.js";
|
||||||
|
import {
|
||||||
|
CustomError,
|
||||||
|
HttpException,
|
||||||
|
} from "./exceptions.js";
|
||||||
|
|
||||||
// Script variables.
|
// Script variables.
|
||||||
const PATs = Object.keys(process.env).filter((key) =>
|
const PATs = Object.keys(process.env).filter((key) =>
|
||||||
@@ -16,8 +20,13 @@ const RETRIES = PATs ? PATs : 7;
|
|||||||
* @returns Promise<retryer>
|
* @returns Promise<retryer>
|
||||||
*/
|
*/
|
||||||
const retryer = async (fetcher, variables, retries = 0) => {
|
const retryer = async (fetcher, variables, retries = 0) => {
|
||||||
if (retries > RETRIES) {
|
// if (retries > RETRIES) {
|
||||||
throw new CustomError("Maximum retries exceeded", CustomError.MAX_RETRY);
|
if (true) { // FIXME: Test out error
|
||||||
|
throw new HttpException(
|
||||||
|
statusCode=500,
|
||||||
|
message=`Max GraphQL retries exceeded. Please add an env variable called PAT_1 with your github token in vercel.`,
|
||||||
|
errors=[new CustomError("Maximum retries exceeded", CustomError.MAX_RETRY)]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// try to fetch with the first token since RETRIES is 0 index i'm adding +1
|
// try to fetch with the first token since RETRIES is 0 index i'm adding +1
|
||||||
@@ -60,5 +69,5 @@ const retryer = async (fetcher, variables, retries = 0) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { retryer };
|
export { retryer, RETRIES };
|
||||||
export default retryer;
|
export default retryer;
|
||||||
|
|||||||
+12
-63
@@ -22,8 +22,7 @@ const renderError = (message, secondaryMessage = "") => {
|
|||||||
.small { font: 600 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: #252525 }
|
.small { font: 600 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: #252525 }
|
||||||
.gray { fill: #858585 }
|
.gray { fill: #858585 }
|
||||||
</style>
|
</style>
|
||||||
<rect x="0.5" y="0.5" width="${
|
<rect x="0.5" y="0.5" width="${ERROR_CARD_LENGTH - 1
|
||||||
ERROR_CARD_LENGTH - 1
|
|
||||||
}" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
|
}" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
|
||||||
<text x="25" y="45" class="text">Something went wrong! file an issue at https://tiny.one/readme-stats</text>
|
<text x="25" y="45" class="text">Something went wrong! file an issue at https://tiny.one/readme-stats</text>
|
||||||
<text data-testid="message" x="25" y="55" class="text small">
|
<text data-testid="message" x="25" y="55" class="text small">
|
||||||
@@ -288,62 +287,28 @@ const wrapTextMultiline = (text, width = 59, maxLines = 3) => {
|
|||||||
return multiLineText;
|
return multiLineText;
|
||||||
};
|
};
|
||||||
|
|
||||||
const noop = () => {};
|
const noop = () => { };
|
||||||
// return console instance based on the environment
|
// return console instance based on the environment
|
||||||
const logger =
|
const logger =
|
||||||
process.env.NODE_ENV !== "test" ? console : { log: noop, error: noop };
|
process.env.NODE_ENV !== "test" ? console : { log: noop, error: noop };
|
||||||
|
|
||||||
|
// Cache settings.
|
||||||
|
const CARD_CACHE_SECONDS = 14400;
|
||||||
|
const ERROR_CACHE_SECONDS = 600;
|
||||||
|
|
||||||
const CONSTANTS = {
|
const CONSTANTS = {
|
||||||
|
ONE_MINUTE: 60,
|
||||||
|
FIVE_MINUTES: 300,
|
||||||
|
TEN_MINUTES: 600,
|
||||||
|
FIFTEEN_MINUTES: 900,
|
||||||
THIRTY_MINUTES: 1800,
|
THIRTY_MINUTES: 1800,
|
||||||
TWO_HOURS: 7200,
|
TWO_HOURS: 7200,
|
||||||
FOUR_HOURS: 14400,
|
FOUR_HOURS: 14400,
|
||||||
ONE_DAY: 86400,
|
ONE_DAY: 86400,
|
||||||
|
CARD_CACHE_SECONDS: CARD_CACHE_SECONDS,
|
||||||
|
ERROR_CACHE_SECONDS: ERROR_CACHE_SECONDS,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SECONDARY_ERROR_MESSAGES = {
|
|
||||||
MAX_RETRY:
|
|
||||||
"Please add an env variable called PAT_1 with your github token in vercel",
|
|
||||||
USER_NOT_FOUND: "Make sure the provided username is not an organization",
|
|
||||||
GRAPHQL_ERROR: "Please try again later",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom error class to handle custom GRS errors.
|
|
||||||
*/
|
|
||||||
class CustomError extends Error {
|
|
||||||
/**
|
|
||||||
* @param {string} message Error message.
|
|
||||||
* @param {string} type Error type.
|
|
||||||
*/
|
|
||||||
constructor(message, type) {
|
|
||||||
super(message);
|
|
||||||
this.type = type;
|
|
||||||
this.secondaryMessage = SECONDARY_ERROR_MESSAGES[type] || type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static MAX_RETRY = "MAX_RETRY";
|
|
||||||
static USER_NOT_FOUND = "USER_NOT_FOUND";
|
|
||||||
static GRAPHQL_ERROR = "GRAPHQL_ERROR";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Missing query parameter class.
|
|
||||||
*/
|
|
||||||
class MissingParamError extends Error {
|
|
||||||
/**
|
|
||||||
* @param {string[]} missedParams
|
|
||||||
* @param {string?=} secondaryMessage
|
|
||||||
*/
|
|
||||||
constructor(missedParams, secondaryMessage) {
|
|
||||||
const msg = `Missing params ${missedParams
|
|
||||||
.map((p) => `"${p}"`)
|
|
||||||
.join(", ")} make sure you pass the parameters in URL`;
|
|
||||||
super(msg);
|
|
||||||
this.missedParams = missedParams;
|
|
||||||
this.secondaryMessage = secondaryMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve text length.
|
* Retrieve text length.
|
||||||
*
|
*
|
||||||
@@ -424,19 +389,6 @@ const parseEmojis = (str) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get diff in minutes
|
|
||||||
* @param {Date} d1
|
|
||||||
* @param {Date} d2
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
const dateDiff = (d1, d2) => {
|
|
||||||
const date1 = new Date(d1);
|
|
||||||
const date2 = new Date(d2);
|
|
||||||
const diff = date1.getTime() - date2.getTime();
|
|
||||||
return Math.round(diff / (1000 * 60));
|
|
||||||
};
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ERROR_CARD_LENGTH,
|
ERROR_CARD_LENGTH,
|
||||||
renderError,
|
renderError,
|
||||||
@@ -454,11 +406,8 @@ export {
|
|||||||
wrapTextMultiline,
|
wrapTextMultiline,
|
||||||
logger,
|
logger,
|
||||||
CONSTANTS,
|
CONSTANTS,
|
||||||
CustomError,
|
|
||||||
MissingParamError,
|
|
||||||
measureText,
|
measureText,
|
||||||
lowercaseTrim,
|
lowercaseTrim,
|
||||||
chunkArray,
|
chunkArray,
|
||||||
parseEmojis,
|
parseEmojis,
|
||||||
dateDiff,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { retryer } from "../common/retryer.js";
|
import { retryer } from "../common/retryer.js";
|
||||||
import { MissingParamError, request } from "../common/utils.js";
|
import { request } from "../common/utils.js";
|
||||||
|
import { MissingParamError } from "../common/exceptions.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repo data fetcher.
|
* Repo data fetcher.
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ import githubUsernameRegex from "github-username-regex";
|
|||||||
import { calculateRank } from "../calculateRank.js";
|
import { calculateRank } from "../calculateRank.js";
|
||||||
import { retryer } from "../common/retryer.js";
|
import { retryer } from "../common/retryer.js";
|
||||||
import {
|
import {
|
||||||
CustomError,
|
|
||||||
logger,
|
logger,
|
||||||
MissingParamError,
|
|
||||||
request,
|
request,
|
||||||
wrapTextMultiline,
|
wrapTextMultiline,
|
||||||
} from "../common/utils.js";
|
} from "../common/utils.js";
|
||||||
|
import { CustomError, MissingParamError } from "../common/exceptions.js";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { retryer } from "../common/retryer.js";
|
import { retryer } from "../common/retryer.js";
|
||||||
import {
|
import {
|
||||||
CustomError,
|
|
||||||
logger,
|
logger,
|
||||||
MissingParamError,
|
|
||||||
request,
|
request,
|
||||||
wrapTextMultiline,
|
wrapTextMultiline,
|
||||||
} from "../common/utils.js";
|
} from "../common/utils.js";
|
||||||
|
import { CustomError, MissingParamError } from "../common/exceptions.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top languages fetcher object.
|
* Top languages fetcher object.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { MissingParamError } from "../common/utils.js";
|
import { MissingParamError } from "../common/exceptions.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WakaTime data fetcher.
|
* WakaTime data fetcher.
|
||||||
|
|||||||
@@ -69,29 +69,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes slideInAnimation {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: calc(100%-100px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes growWidthAnimation {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
|
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
|
||||||
#rect-mask rect{
|
|
||||||
animation: slideInAnimation 1s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
.lang-progress{
|
|
||||||
animation: growWidthAnimation 0.6s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -249,29 +227,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout w
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes slideInAnimation {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: calc(100%-100px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes growWidthAnimation {
|
|
||||||
from {
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
|
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
|
||||||
#rect-mask rect{
|
|
||||||
animation: slideInAnimation 1s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
.lang-progress{
|
|
||||||
animation: growWidthAnimation 0.6s ease-in-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -171,13 +171,18 @@ describe("Test /api/", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not store cache when error", async () => {
|
it("should set shorter cache when error", async () => {
|
||||||
const { req, res } = faker({}, error);
|
const { req, res } = faker({}, error);
|
||||||
await api(req, res);
|
await api(req, res);
|
||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
expect(res.setHeader.mock.calls).toEqual([
|
||||||
["Content-Type", "image/svg+xml"],
|
["Content-Type", "image/svg+xml"],
|
||||||
["Cache-Control", `no-cache, no-store, must-revalidate`],
|
[
|
||||||
|
"Cache-Control",
|
||||||
|
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
|
||||||
|
CONSTANTS.ERROR_CACHE_SECONDS
|
||||||
|
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ const REPO = "curly-fiesta";
|
|||||||
const USER = "catelinemnemosyne";
|
const USER = "catelinemnemosyne";
|
||||||
const STATS_DATA = {
|
const STATS_DATA = {
|
||||||
name: "Cateline Mnemosyne",
|
name: "Cateline Mnemosyne",
|
||||||
totalPRs: 2,
|
totalPRs: 1,
|
||||||
totalCommits: 8,
|
totalCommits: 7,
|
||||||
totalIssues: 1,
|
totalIssues: 1,
|
||||||
totalStars: 1,
|
totalStars: 1,
|
||||||
contributedTo: 1,
|
contributedTo: 1,
|
||||||
rank: {
|
rank: {
|
||||||
level: "A+",
|
level: "A+",
|
||||||
score: 50.88831151384285,
|
score: 50.893750297869225,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,244 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Tests for the status/pat-info cloud function.
|
|
||||||
*/
|
|
||||||
import dotenv from "dotenv";
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
import { jest } from "@jest/globals";
|
|
||||||
import axios from "axios";
|
|
||||||
import MockAdapter from "axios-mock-adapter";
|
|
||||||
import patInfo, { RATE_LIMIT_SECONDS } from "../api/status/pat-info.js";
|
|
||||||
|
|
||||||
const mock = new MockAdapter(axios);
|
|
||||||
|
|
||||||
const successData = {
|
|
||||||
data: {
|
|
||||||
rateLimit: {
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const faker = (query) => {
|
|
||||||
const req = {
|
|
||||||
query: { ...query },
|
|
||||||
};
|
|
||||||
const res = {
|
|
||||||
setHeader: jest.fn(),
|
|
||||||
send: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return { req, res };
|
|
||||||
};
|
|
||||||
|
|
||||||
const rate_limit_error = {
|
|
||||||
errors: [
|
|
||||||
{
|
|
||||||
type: "RATE_LIMITED",
|
|
||||||
message: "API rate limit exceeded for user ID.",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
data: {
|
|
||||||
rateLimit: {
|
|
||||||
resetAt: Date.now(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const other_error = {
|
|
||||||
errors: [
|
|
||||||
{
|
|
||||||
type: "SOME_ERROR",
|
|
||||||
message: "This is a error",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const bad_credentials_error = {
|
|
||||||
message: "Bad credentials",
|
|
||||||
};
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
mock.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Test /api/status/pat-info", () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
// reset patenv first so that dotenv doesn't populate them with local envs
|
|
||||||
process.env = {};
|
|
||||||
process.env.PAT_1 = "testPAT1";
|
|
||||||
process.env.PAT_2 = "testPAT2";
|
|
||||||
process.env.PAT_3 = "testPAT3";
|
|
||||||
process.env.PAT_4 = "testPAT4";
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return only 'validPATs' if all PATs are valid", async () => {
|
|
||||||
mock
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.replyOnce(200, rate_limit_error)
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.reply(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await patInfo(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
validPATs: ["PAT_2", "PAT_3", "PAT_4"],
|
|
||||||
expiredPATs: [],
|
|
||||||
exhaustedPATs: ["PAT_1"],
|
|
||||||
suspendedPATs: [],
|
|
||||||
errorPATs: [],
|
|
||||||
details: {
|
|
||||||
PAT_1: {
|
|
||||||
status: "exhausted",
|
|
||||||
remaining: 0,
|
|
||||||
resetIn: "0 minutes",
|
|
||||||
},
|
|
||||||
PAT_2: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
PAT_3: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
PAT_4: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return `errorPATs` if a PAT causes an error to be thrown", async () => {
|
|
||||||
mock
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.replyOnce(200, other_error)
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.reply(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await patInfo(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
validPATs: ["PAT_2", "PAT_3", "PAT_4"],
|
|
||||||
expiredPATs: [],
|
|
||||||
exhaustedPATs: [],
|
|
||||||
suspendedPATs: [],
|
|
||||||
errorPATs: ["PAT_1"],
|
|
||||||
details: {
|
|
||||||
PAT_1: {
|
|
||||||
status: "error",
|
|
||||||
error: {
|
|
||||||
type: "SOME_ERROR",
|
|
||||||
message: "This is a error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PAT_2: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
PAT_3: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
PAT_4: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return `expiredPaths` if a PAT returns a 'Bad credentials' error", async () => {
|
|
||||||
mock
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.replyOnce(404, bad_credentials_error)
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.reply(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await patInfo(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
validPATs: ["PAT_2", "PAT_3", "PAT_4"],
|
|
||||||
expiredPATs: ["PAT_1"],
|
|
||||||
exhaustedPATs: [],
|
|
||||||
suspendedPATs: [],
|
|
||||||
errorPATs: [],
|
|
||||||
details: {
|
|
||||||
PAT_1: {
|
|
||||||
status: "expired",
|
|
||||||
},
|
|
||||||
PAT_2: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
PAT_3: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
PAT_4: {
|
|
||||||
status: "valid",
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should throw an error if something goes wrong", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").networkError();
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await patInfo(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith("Something went wrong: Network Error");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have proper cache when no error is thrown", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").reply(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await patInfo(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
|
||||||
["Content-Type", "application/json"],
|
|
||||||
["Cache-Control", `max-age=0, s-maxage=${RATE_LIMIT_SECONDS}`],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have proper cache when error is thrown", async () => {
|
|
||||||
mock.reset();
|
|
||||||
mock.onPost("https://api.github.com/graphql").networkError();
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await patInfo(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
|
||||||
["Content-Type", "application/json"],
|
|
||||||
["Cache-Control", "no-store"],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -78,17 +78,16 @@ describe("Test renderStatsCard", () => {
|
|||||||
|
|
||||||
it("should render with custom width set", () => {
|
it("should render with custom width set", () => {
|
||||||
document.body.innerHTML = renderStatsCard(stats);
|
document.body.innerHTML = renderStatsCard(stats);
|
||||||
expect(document.querySelector("svg")).toHaveAttribute("width", "450");
|
expect(document.querySelector("svg")).toHaveAttribute("width", "495");
|
||||||
|
|
||||||
document.body.innerHTML = renderStatsCard(stats, { card_width: 500 });
|
document.body.innerHTML = renderStatsCard(stats, { card_width: 400 });
|
||||||
expect(document.querySelector("svg")).toHaveAttribute("width", "500");
|
expect(document.querySelector("svg")).toHaveAttribute("width", "400");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render with custom width set and limit minimum width", () => {
|
it("should render with custom width set and limit minimum width", () => {
|
||||||
document.body.innerHTML = renderStatsCard(stats, { card_width: 1 });
|
document.body.innerHTML = renderStatsCard(stats, { card_width: 1 });
|
||||||
expect(document.querySelector("svg")).toHaveAttribute("width", "420");
|
expect(document.querySelector("svg")).toHaveAttribute("width", "340");
|
||||||
|
|
||||||
// Test default minimum card width without rank circle.
|
|
||||||
document.body.innerHTML = renderStatsCard(stats, {
|
document.body.innerHTML = renderStatsCard(stats, {
|
||||||
card_width: 1,
|
card_width: 1,
|
||||||
hide_rank: true,
|
hide_rank: true,
|
||||||
@@ -98,7 +97,6 @@ describe("Test renderStatsCard", () => {
|
|||||||
"305.81250000000006",
|
"305.81250000000006",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test minimum card width with rank and icons.
|
|
||||||
document.body.innerHTML = renderStatsCard(stats, {
|
document.body.innerHTML = renderStatsCard(stats, {
|
||||||
card_width: 1,
|
card_width: 1,
|
||||||
hide_rank: true,
|
hide_rank: true,
|
||||||
@@ -106,24 +104,22 @@ describe("Test renderStatsCard", () => {
|
|||||||
});
|
});
|
||||||
expect(document.querySelector("svg")).toHaveAttribute(
|
expect(document.querySelector("svg")).toHaveAttribute(
|
||||||
"width",
|
"width",
|
||||||
"322.81250000000006",
|
"305.81250000000006",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test minimum card width with icons but without rank.
|
|
||||||
document.body.innerHTML = renderStatsCard(stats, {
|
document.body.innerHTML = renderStatsCard(stats, {
|
||||||
card_width: 1,
|
card_width: 1,
|
||||||
hide_rank: false,
|
hide_rank: false,
|
||||||
show_icons: true,
|
show_icons: true,
|
||||||
});
|
});
|
||||||
expect(document.querySelector("svg")).toHaveAttribute("width", "437");
|
expect(document.querySelector("svg")).toHaveAttribute("width", "356");
|
||||||
|
|
||||||
// Test minimum card width without icons or rank.
|
|
||||||
document.body.innerHTML = renderStatsCard(stats, {
|
document.body.innerHTML = renderStatsCard(stats, {
|
||||||
card_width: 1,
|
card_width: 1,
|
||||||
hide_rank: false,
|
hide_rank: false,
|
||||||
show_icons: false,
|
show_icons: false,
|
||||||
});
|
});
|
||||||
expect(document.querySelector("svg")).toHaveAttribute("width", "420");
|
expect(document.querySelector("svg")).toHaveAttribute("width", "340");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render default colors properly", () => {
|
it("should render default colors properly", () => {
|
||||||
@@ -316,7 +312,7 @@ describe("Test renderStatsCard", () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
document.body.getElementsByTagName("svg")[0].getAttribute("width"),
|
document.body.getElementsByTagName("svg")[0].getAttribute("width"),
|
||||||
).toBe("287");
|
).toBe("270");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render translations", () => {
|
it("should render translations", () => {
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ describe("Test renderTopLanguages", () => {
|
|||||||
);
|
);
|
||||||
expect(queryAllByTestId(document.body, "lang-progress")[0]).toHaveAttribute(
|
expect(queryAllByTestId(document.body, "lang-progress")[0]).toHaveAttribute(
|
||||||
"width",
|
"width",
|
||||||
"100",
|
"120",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
|
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
|
||||||
@@ -224,7 +224,7 @@ describe("Test renderTopLanguages", () => {
|
|||||||
);
|
);
|
||||||
expect(queryAllByTestId(document.body, "lang-progress")[1]).toHaveAttribute(
|
expect(queryAllByTestId(document.body, "lang-progress")[1]).toHaveAttribute(
|
||||||
"width",
|
"width",
|
||||||
"100",
|
"120",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
|
expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
|
||||||
@@ -232,7 +232,7 @@ describe("Test renderTopLanguages", () => {
|
|||||||
);
|
);
|
||||||
expect(queryAllByTestId(document.body, "lang-progress")[2]).toHaveAttribute(
|
expect(queryAllByTestId(document.body, "lang-progress")[2]).toHaveAttribute(
|
||||||
"width",
|
"width",
|
||||||
"50",
|
"60",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { jest } from "@jest/globals";
|
import { jest } from "@jest/globals";
|
||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
import { retryer } from "../src/common/retryer.js";
|
import { retryer, RETRIES } from "../src/common/retryer.js";
|
||||||
import { logger } from "../src/common/utils.js";
|
import { logger } from "../src/common/utils.js";
|
||||||
|
|
||||||
const fetcher = jest.fn((variables, token) => {
|
const fetcher = jest.fn((variables, token) => {
|
||||||
@@ -45,7 +45,7 @@ describe("Test Retryer", () => {
|
|||||||
try {
|
try {
|
||||||
res = await retryer(fetcherFail, {});
|
res = await retryer(fetcherFail, {});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(fetcherFail).toBeCalledTimes(8);
|
expect(fetcherFail).toBeCalledTimes(RETRIES + 1);
|
||||||
expect(err.message).toBe("Maximum retries exceeded");
|
expect(err.message).toBe("Maximum retries exceeded");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,194 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Tests for the status/up cloud function.
|
|
||||||
*/
|
|
||||||
import { jest } from "@jest/globals";
|
|
||||||
import axios from "axios";
|
|
||||||
import MockAdapter from "axios-mock-adapter";
|
|
||||||
import up, { RATE_LIMIT_SECONDS } from "../api/status/up.js";
|
|
||||||
|
|
||||||
const mock = new MockAdapter(axios);
|
|
||||||
|
|
||||||
const successData = {
|
|
||||||
rateLimit: {
|
|
||||||
remaining: 4986,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const faker = (query) => {
|
|
||||||
const req = {
|
|
||||||
query: { ...query },
|
|
||||||
};
|
|
||||||
const res = {
|
|
||||||
setHeader: jest.fn(),
|
|
||||||
send: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return { req, res };
|
|
||||||
};
|
|
||||||
|
|
||||||
const rate_limit_error = {
|
|
||||||
errors: [
|
|
||||||
{
|
|
||||||
type: "RATE_LIMITED",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const bad_credentials_error = {
|
|
||||||
message: "Bad credentials",
|
|
||||||
};
|
|
||||||
|
|
||||||
const shields_up = {
|
|
||||||
schemaVersion: 1,
|
|
||||||
label: "Public Instance",
|
|
||||||
isError: true,
|
|
||||||
message: "up",
|
|
||||||
color: "brightgreen",
|
|
||||||
};
|
|
||||||
const shields_down = {
|
|
||||||
schemaVersion: 1,
|
|
||||||
label: "Public Instance",
|
|
||||||
isError: true,
|
|
||||||
message: "down",
|
|
||||||
color: "red",
|
|
||||||
};
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
mock.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Test /api/status/up", () => {
|
|
||||||
it("should return `true` if request was successful", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").replyOnce(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return `false` if all PATs are rate limited", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").reply(200, rate_limit_error);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return JSON `true` if request was successful and type='json'", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").replyOnce(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({ type: "json" }, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith({ up: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return JSON `false` if all PATs are rate limited and type='json'", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").reply(200, rate_limit_error);
|
|
||||||
|
|
||||||
const { req, res } = faker({ type: "json" }, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith({ up: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return UP shields.io config if request was successful and type='shields'", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").replyOnce(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({ type: "shields" }, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(shields_up);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return DOWN shields.io config if all PATs are rate limited and type='shields'", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").reply(200, rate_limit_error);
|
|
||||||
|
|
||||||
const { req, res } = faker({ type: "shields" }, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(shields_down);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return `true` if the first PAT is rate limited but the second PATs works", async () => {
|
|
||||||
mock
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.replyOnce(200, rate_limit_error)
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.replyOnce(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return `true` if the first PAT has 'Bad credentials' but the second PAT works", async () => {
|
|
||||||
mock
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.replyOnce(404, bad_credentials_error)
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.replyOnce(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return `false` if all pats have 'Bad credentials'", async () => {
|
|
||||||
mock
|
|
||||||
.onPost("https://api.github.com/graphql")
|
|
||||||
.reply(404, bad_credentials_error);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should throw an error if the request fails", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").networkError();
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader).toBeCalledWith("Content-Type", "application/json");
|
|
||||||
expect(res.send).toBeCalledWith(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have proper cache when no error is thrown", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").replyOnce(200, successData);
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
|
||||||
["Content-Type", "application/json"],
|
|
||||||
["Cache-Control", `max-age=0, s-maxage=${RATE_LIMIT_SECONDS}`],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have proper cache when error is thrown", async () => {
|
|
||||||
mock.onPost("https://api.github.com/graphql").networkError();
|
|
||||||
|
|
||||||
const { req, res } = faker({}, {});
|
|
||||||
await up(req, res);
|
|
||||||
|
|
||||||
expect(res.setHeader.mock.calls).toEqual([
|
|
||||||
["Content-Type", "application/json"],
|
|
||||||
["Cache-Control", "no-store"],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
-174
@@ -12,12 +12,6 @@ export const themes = {
|
|||||||
text_color: "434d58",
|
text_color: "434d58",
|
||||||
bg_color: "fffefe",
|
bg_color: "fffefe",
|
||||||
},
|
},
|
||||||
"default-repocard": {
|
|
||||||
title_color: "2f80ed",
|
|
||||||
icon_color: "586069", // icon color is different
|
|
||||||
text_color: "434d58",
|
|
||||||
bg_color: "fffefe",
|
|
||||||
},
|
|
||||||
transparent: {
|
transparent: {
|
||||||
title_color: "006AFF",
|
title_color: "006AFF",
|
||||||
icon_color: "0579C3",
|
icon_color: "0579C3",
|
||||||
@@ -54,12 +48,6 @@ export const themes = {
|
|||||||
text_color: "427b58",
|
text_color: "427b58",
|
||||||
bg_color: "fbf1c7",
|
bg_color: "fbf1c7",
|
||||||
},
|
},
|
||||||
"gruvbox-light": {
|
|
||||||
title_color: "b57614",
|
|
||||||
icon_color: "af3a03",
|
|
||||||
text_color: "427b58",
|
|
||||||
bg_color: "fbf1c7",
|
|
||||||
},
|
|
||||||
tokyonight: {
|
tokyonight: {
|
||||||
title_color: "70a5fd",
|
title_color: "70a5fd",
|
||||||
icon_color: "bf91f3",
|
icon_color: "bf91f3",
|
||||||
@@ -120,24 +108,12 @@ export const themes = {
|
|||||||
text_color: "fffefe",
|
text_color: "fffefe",
|
||||||
bg_color: "273849",
|
bg_color: "273849",
|
||||||
},
|
},
|
||||||
vue_dark: {
|
|
||||||
title_color: "41b883",
|
|
||||||
icon_color: "41b883",
|
|
||||||
text_color: "fffefe",
|
|
||||||
bg_color: "273849",
|
|
||||||
},
|
|
||||||
"shades-of-purple": {
|
"shades-of-purple": {
|
||||||
title_color: "fad000",
|
title_color: "fad000",
|
||||||
icon_color: "b362ff",
|
icon_color: "b362ff",
|
||||||
text_color: "a599e9",
|
text_color: "a599e9",
|
||||||
bg_color: "2d2b55",
|
bg_color: "2d2b55",
|
||||||
},
|
},
|
||||||
shades_of_purple: {
|
|
||||||
title_color: "fad000",
|
|
||||||
icon_color: "b362ff",
|
|
||||||
text_color: "a599e9",
|
|
||||||
bg_color: "2d2b55",
|
|
||||||
},
|
|
||||||
nightowl: {
|
nightowl: {
|
||||||
title_color: "c792ea",
|
title_color: "c792ea",
|
||||||
icon_color: "ffeb95",
|
icon_color: "ffeb95",
|
||||||
@@ -156,12 +132,6 @@ export const themes = {
|
|||||||
text_color: "0cf574",
|
text_color: "0cf574",
|
||||||
bg_color: "040f0f",
|
bg_color: "040f0f",
|
||||||
},
|
},
|
||||||
blue_green: {
|
|
||||||
title_color: "2f97c1",
|
|
||||||
icon_color: "f5b700",
|
|
||||||
text_color: "0cf574",
|
|
||||||
bg_color: "040f0f",
|
|
||||||
},
|
|
||||||
algolia: {
|
algolia: {
|
||||||
title_color: "00AEFF",
|
title_color: "00AEFF",
|
||||||
icon_color: "2DDE98",
|
icon_color: "2DDE98",
|
||||||
@@ -174,12 +144,6 @@ export const themes = {
|
|||||||
text_color: "ffd95b",
|
text_color: "ffd95b",
|
||||||
bg_color: "000000",
|
bg_color: "000000",
|
||||||
},
|
},
|
||||||
great_gatsby: {
|
|
||||||
title_color: "ffa726",
|
|
||||||
icon_color: "ffb74d",
|
|
||||||
text_color: "ffd95b",
|
|
||||||
bg_color: "000000",
|
|
||||||
},
|
|
||||||
darcula: {
|
darcula: {
|
||||||
title_color: "BA5F17",
|
title_color: "BA5F17",
|
||||||
icon_color: "84628F",
|
icon_color: "84628F",
|
||||||
@@ -210,24 +174,6 @@ export const themes = {
|
|||||||
text_color: "fff",
|
text_color: "fff",
|
||||||
bg_color: "000",
|
bg_color: "000",
|
||||||
},
|
},
|
||||||
solarized_dark: {
|
|
||||||
title_color: "268bd2",
|
|
||||||
icon_color: "b58900",
|
|
||||||
text_color: "859900",
|
|
||||||
bg_color: "002b36",
|
|
||||||
},
|
|
||||||
solarized_light: {
|
|
||||||
title_color: "268bd2",
|
|
||||||
icon_color: "b58900",
|
|
||||||
text_color: "859900",
|
|
||||||
bg_color: "fdf6e3",
|
|
||||||
},
|
|
||||||
chartreuse_dark: {
|
|
||||||
title_color: "7fff00",
|
|
||||||
icon_color: "00AEFF",
|
|
||||||
text_color: "fff",
|
|
||||||
bg_color: "000",
|
|
||||||
},
|
|
||||||
nord: {
|
nord: {
|
||||||
title_color: "81a1c1",
|
title_color: "81a1c1",
|
||||||
text_color: "d8dee9",
|
text_color: "d8dee9",
|
||||||
@@ -246,12 +192,6 @@ export const themes = {
|
|||||||
text_color: "a6accd",
|
text_color: "a6accd",
|
||||||
bg_color: "292d3e",
|
bg_color: "292d3e",
|
||||||
},
|
},
|
||||||
material_palenight: {
|
|
||||||
title_color: "c792ea",
|
|
||||||
icon_color: "89ddff",
|
|
||||||
text_color: "a6accd",
|
|
||||||
bg_color: "292d3e",
|
|
||||||
},
|
|
||||||
graywhite: {
|
graywhite: {
|
||||||
title_color: "24292e",
|
title_color: "24292e",
|
||||||
icon_color: "24292e",
|
icon_color: "24292e",
|
||||||
@@ -276,24 +216,6 @@ export const themes = {
|
|||||||
text_color: "ffffff",
|
text_color: "ffffff",
|
||||||
bg_color: "000000",
|
bg_color: "000000",
|
||||||
},
|
},
|
||||||
vision_friendly_dark: {
|
|
||||||
title_color: "ffb000",
|
|
||||||
icon_color: "785ef0",
|
|
||||||
text_color: "ffffff",
|
|
||||||
bg_color: "000000",
|
|
||||||
},
|
|
||||||
ayu_mirage: {
|
|
||||||
title_color: "f4cd7c",
|
|
||||||
icon_color: "73d0ff",
|
|
||||||
text_color: "c7c8c2",
|
|
||||||
bg_color: "1f2430",
|
|
||||||
},
|
|
||||||
midnight_purple: {
|
|
||||||
title_color: "9745f5",
|
|
||||||
icon_color: "9f4bff",
|
|
||||||
text_color: "ffffff",
|
|
||||||
bg_color: "000000",
|
|
||||||
},
|
|
||||||
calm: {
|
calm: {
|
||||||
title_color: "e07a5f",
|
title_color: "e07a5f",
|
||||||
icon_color: "edae49",
|
icon_color: "edae49",
|
||||||
@@ -306,12 +228,6 @@ export const themes = {
|
|||||||
text_color: "509E2F",
|
text_color: "509E2F",
|
||||||
bg_color: "ffffff",
|
bg_color: "ffffff",
|
||||||
},
|
},
|
||||||
flag_india: {
|
|
||||||
title_color: "ff8f1c",
|
|
||||||
icon_color: "250E62",
|
|
||||||
text_color: "509E2F",
|
|
||||||
bg_color: "ffffff",
|
|
||||||
},
|
|
||||||
omni: {
|
omni: {
|
||||||
title_color: "FF79C6",
|
title_color: "FF79C6",
|
||||||
icon_color: "e7de79",
|
icon_color: "e7de79",
|
||||||
@@ -360,12 +276,6 @@ export const themes = {
|
|||||||
text_color: "d9c8a9",
|
text_color: "d9c8a9",
|
||||||
bg_color: "402b23",
|
bg_color: "402b23",
|
||||||
},
|
},
|
||||||
"kacho-ga": {
|
|
||||||
title_color: "bf4a3f",
|
|
||||||
icon_color: "a64833",
|
|
||||||
text_color: "d9c8a9",
|
|
||||||
bg_color: "402b23",
|
|
||||||
},
|
|
||||||
outrun: {
|
outrun: {
|
||||||
title_color: "ffcc00",
|
title_color: "ffcc00",
|
||||||
icon_color: "ff1aff",
|
icon_color: "ff1aff",
|
||||||
@@ -384,18 +294,6 @@ export const themes = {
|
|||||||
text_color: "718CA1",
|
text_color: "718CA1",
|
||||||
bg_color: "1D252C",
|
bg_color: "1D252C",
|
||||||
},
|
},
|
||||||
"ocean-dark": {
|
|
||||||
title_color: "8957B2",
|
|
||||||
icon_color: "FFFFFF",
|
|
||||||
text_color: "92D534",
|
|
||||||
bg_color: "151A28",
|
|
||||||
},
|
|
||||||
"city-lights": {
|
|
||||||
title_color: "5D8CB3",
|
|
||||||
icon_color: "4798FF",
|
|
||||||
text_color: "718CA1",
|
|
||||||
bg_color: "1D252C",
|
|
||||||
},
|
|
||||||
github_dark: {
|
github_dark: {
|
||||||
title_color: "58A6FF",
|
title_color: "58A6FF",
|
||||||
icon_color: "1F6FEB",
|
icon_color: "1F6FEB",
|
||||||
@@ -414,24 +312,6 @@ export const themes = {
|
|||||||
text_color: "dbdbdb",
|
text_color: "dbdbdb",
|
||||||
bg_color: "252334",
|
bg_color: "252334",
|
||||||
},
|
},
|
||||||
"github-dark": {
|
|
||||||
title_color: "58A6FF",
|
|
||||||
icon_color: "1F6FEB",
|
|
||||||
text_color: "C3D1D9",
|
|
||||||
bg_color: "0D1117",
|
|
||||||
},
|
|
||||||
"discord-old-blurple": {
|
|
||||||
title_color: "7289DA",
|
|
||||||
icon_color: "7289DA",
|
|
||||||
text_color: "FFFFFF",
|
|
||||||
bg_color: "2C2F33",
|
|
||||||
},
|
|
||||||
"aura-dark": {
|
|
||||||
title_color: "ff7372",
|
|
||||||
icon_color: "6cffd0",
|
|
||||||
text_color: "dbdbdb",
|
|
||||||
bg_color: "252334",
|
|
||||||
},
|
|
||||||
panda: {
|
panda: {
|
||||||
title_color: "19f9d899",
|
title_color: "19f9d899",
|
||||||
icon_color: "19f9d899",
|
icon_color: "19f9d899",
|
||||||
@@ -444,12 +324,6 @@ export const themes = {
|
|||||||
text_color: "c5cdd3",
|
text_color: "c5cdd3",
|
||||||
bg_color: "1b2932",
|
bg_color: "1b2932",
|
||||||
},
|
},
|
||||||
"noctis-minimus": {
|
|
||||||
title_color: "d3b692",
|
|
||||||
icon_color: "72b7c0",
|
|
||||||
text_color: "c5cdd3",
|
|
||||||
bg_color: "1b2932",
|
|
||||||
},
|
|
||||||
cobalt2: {
|
cobalt2: {
|
||||||
title_color: "ffc600",
|
title_color: "ffc600",
|
||||||
icon_color: "ffffff",
|
icon_color: "ffffff",
|
||||||
@@ -500,54 +374,6 @@ export const themes = {
|
|||||||
border_color: "170F0C",
|
border_color: "170F0C",
|
||||||
bg_color: "170F0C",
|
bg_color: "170F0C",
|
||||||
},
|
},
|
||||||
one_dark_pro: {
|
|
||||||
title_color: "61AFEF",
|
|
||||||
text_color: "E5C06E",
|
|
||||||
icon_color: "C678DD",
|
|
||||||
border_color: "3B4048",
|
|
||||||
bg_color: "23272E",
|
|
||||||
},
|
|
||||||
"rose-pine": {
|
|
||||||
title_color: "9ccfd8",
|
|
||||||
icon_color: "ebbcba",
|
|
||||||
text_color: "e0def4",
|
|
||||||
bg_color: "191724",
|
|
||||||
},
|
|
||||||
"date-night": {
|
|
||||||
title_color: "DA7885",
|
|
||||||
text_color: "E1B2A2",
|
|
||||||
icon_color: "BB8470",
|
|
||||||
border_color: "170F0C",
|
|
||||||
bg_color: "170F0C",
|
|
||||||
},
|
|
||||||
"one-dark-pro": {
|
|
||||||
title_color: "61AFEF",
|
|
||||||
text_color: "E5C06E",
|
|
||||||
icon_color: "C678DD",
|
|
||||||
border_color: "3B4048",
|
|
||||||
bg_color: "23272E",
|
|
||||||
},
|
|
||||||
rose: {
|
|
||||||
title_color: "8d192b",
|
|
||||||
text_color: "862931",
|
|
||||||
icon_color: "B71F36",
|
|
||||||
border_color: "e9d8d4",
|
|
||||||
bg_color: "e9d8d4",
|
|
||||||
},
|
|
||||||
holi_theme: {
|
|
||||||
title_color: "5FABEE",
|
|
||||||
text_color: "D6E7FF",
|
|
||||||
icon_color: "5FABEE",
|
|
||||||
border_color: "85A4C0",
|
|
||||||
bg_color: "030314",
|
|
||||||
},
|
|
||||||
"holi-theme": {
|
|
||||||
title_color: "5FABEE",
|
|
||||||
text_color: "D6E7FF",
|
|
||||||
icon_color: "5FABEE",
|
|
||||||
border_color: "85A4C0",
|
|
||||||
bg_color: "030314",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themes;
|
export default themes;
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"functions": {
|
"functions": {
|
||||||
"api/*.js": {
|
"api/*.js": {
|
||||||
"memory": 128,
|
"memory": 128,
|
||||||
"maxDuration": 10
|
"maxDuration": 30
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirects": [
|
"redirects": [
|
||||||
|
|||||||
Reference in New Issue
Block a user