Merge branch 'master' into release
This commit is contained in:
+13
-10
@@ -2,12 +2,15 @@
|
||||
|
||||
## Local Development
|
||||
|
||||
To run and test GitHub-Stats-Extended, you need to follow a few simple steps:
|
||||
To set up the project GitHub-Stats-Extended locally, run the following commands:
|
||||
|
||||
1. create [your own deployment](../docs/deploy.md)
|
||||
2. optional: add an SQL database; by using e.g. the ["Nile" integration](https://vercel.com/marketplace/nile) or by manually setting the environment variable `POSTGRES_URL`
|
||||
3. optional: [create your own OAuth App](https://github.com/settings/developers) and set environment variables `OAUTH_REDIRECT_URI`, `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` on Vercel accordingly
|
||||
4. optional: in addition to the Vercel project based on the `backend` folder, create a second project based on the `frontend/frontend` folder. No environment variables needed.
|
||||
```bash
|
||||
./vercel-preparation.sh
|
||||
pnpm install
|
||||
pnpm --filter frontend run build
|
||||
```
|
||||
|
||||
The easiest way to run and test the project is to deploy it to Vercel as described in the [deployment guide](../docs/deploy.md).
|
||||
|
||||
## Themes Contribution
|
||||
|
||||
@@ -37,9 +40,9 @@ We use GitHub issues to track public bugs. Report a bug by [opening a new issue]
|
||||
|
||||
> **Ans:** Please read all the related issues/comments before opening any issues regarding language card stats:
|
||||
>
|
||||
> - <https://github.com/anuraghazra/github-readme-stats/issues/136#issuecomment-665164174>
|
||||
> - <https://github.com/anuraghazra/github-readme-stats/issues/136#issuecomment-665164174>
|
||||
>
|
||||
> - <https://github.com/anuraghazra/github-readme-stats/issues/136#issuecomment-665172181>
|
||||
> - <https://github.com/anuraghazra/github-readme-stats/issues/136#issuecomment-665172181>
|
||||
|
||||
**Q:** How to count private stats?
|
||||
|
||||
@@ -49,6 +52,6 @@ We use GitHub issues to track public bugs. Report a bug by [opening a new issue]
|
||||
|
||||
**Great Feature Requests** tend to have:
|
||||
|
||||
- A quick idea summary
|
||||
- What & why do you want to add the specific feature
|
||||
- Additional context like images, links to resources to implement the feature, etc.
|
||||
- A quick idea summary
|
||||
- What & why do you want to add the specific feature
|
||||
- Additional context like images, links to resources to implement the feature, etc.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Setup Node.js + PNPM and install Dependencies
|
||||
description: |
|
||||
This is a composite GitHub Action that:
|
||||
- Sets up pnpm package manager
|
||||
- Configures Node.js environment
|
||||
- Installs project dependencies with caching
|
||||
|
||||
inputs:
|
||||
node-version:
|
||||
description: "Explicit node version. Otherwise fallback reading `.nvmrc`"
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
steps:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js (via input)
|
||||
if: ${{ inputs.node-version }}
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
cache: "pnpm"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Setup Node.js (via .nvmrc)
|
||||
if: ${{ !inputs.node-version }}
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "pnpm"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -0,0 +1,43 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Maintain dependencies for npm
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: cron
|
||||
cronjob: every day at 5am
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
prefix-development: "build(deps-dev)"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
ignore:
|
||||
- dependency-name: "@types/node"
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: cron
|
||||
cronjob: every day at 5am
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "ci(deps)"
|
||||
prefix-development: "ci(deps-dev)"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
# Maintain dependencies for Devcontainers
|
||||
- package-ecosystem: devcontainers
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: cron
|
||||
cronjob: every day at 5am
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
prefix-development: "build(deps-dev)"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
@@ -0,0 +1,118 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.head_ref }}"
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: Build and test ${{ matrix.node }} on ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: [24]
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run vercel-preparation.sh
|
||||
run: |
|
||||
chmod +x ./vercel-preparation.sh
|
||||
./vercel-preparation.sh
|
||||
|
||||
- name: Install Dependencies
|
||||
uses: ./.github/actions/install-dependencies
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Build frontend
|
||||
run: pnpm --filter frontend run build
|
||||
|
||||
- name: Run frontend tests
|
||||
run: pnpm --filter frontend run test
|
||||
|
||||
- name: Run backend tests
|
||||
run: pnpm --filter github-readme-stats run test
|
||||
|
||||
frontend-test-e2e:
|
||||
name: Frontend E2E test
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
timeout-minutes: 60
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install Dependencies
|
||||
uses: ./.github/actions/install-dependencies
|
||||
|
||||
- name: Run vercel-preparation.sh
|
||||
run: |
|
||||
chmod +x ./vercel-preparation.sh
|
||||
./vercel-preparation.sh
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install --with-deps
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: pnpm --filter frontend run test:e2e
|
||||
|
||||
code-checks:
|
||||
name: Code checks
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Heads up!
|
||||
#
|
||||
# 1. Execution of this script is needed to resolve `.vercel` folder from `apps/frontend/src/components/Card/SVG.js`
|
||||
# 2. This scripts removes `apps/backend/node_modules` breaking ESLint’s module resolution.
|
||||
# Dependency installation must occur after running ./vercel-preparation.sh.
|
||||
- name: Run vercel-preparation.sh
|
||||
run: |
|
||||
chmod +x ./vercel-preparation.sh
|
||||
./vercel-preparation.sh
|
||||
|
||||
- name: Install Dependencies
|
||||
uses: ./.github/actions/install-dependencies
|
||||
|
||||
- name: Format
|
||||
run: pnpm run format:check
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Lint (knip)
|
||||
run: pnpm run lint:knip
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm run typecheck
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Generate Theme Readme
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "apps/backend/themes/index.js"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
generate-theme-doc:
|
||||
runs-on: ubuntu-latest
|
||||
name: Generate theme doc
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: write
|
||||
deployments: read
|
||||
issues: read
|
||||
discussions: read
|
||||
packages: read
|
||||
pages: read
|
||||
pull-requests: write
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install Dependencies
|
||||
uses: ./.github/actions/install-dependencies
|
||||
|
||||
- name: Generate readme
|
||||
run: |
|
||||
pnpm --filter github-readme-stats run theme-readme-gen
|
||||
|
||||
- name: Create Pull Request if themes README has changed
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
commit-message: "feat(backend): update themes README"
|
||||
branch: "update_themes_readme/patch"
|
||||
delete-branch: true
|
||||
title: Update themes README
|
||||
body: "The
|
||||
[generate-theme-doc](https://github.com/stats-organization/github-stats-extended/actions/workflows/generate-theme-doc.yml)
|
||||
action found new/updated languages in the [upstream languages JSON
|
||||
file](https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml)."
|
||||
labels: "ci"
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
- name: Make Request
|
||||
id: myRequest
|
||||
uses: fjogeleit/http-request-action@v1
|
||||
uses: fjogeleit/http-request-action@v2
|
||||
with:
|
||||
url: "https://github-stats-extended.vercel.app/api/repeat-recent"
|
||||
method: "POST"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
name: Update supported languages
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# ┌───────────── minute (0 - 59)
|
||||
# │ ┌───────────── hour (0 - 23)
|
||||
@@ -27,41 +28,33 @@ permissions:
|
||||
statuses: read
|
||||
|
||||
jobs:
|
||||
updateLanguages:
|
||||
if: github.repository == 'anuraghazra/github-readme-stats'
|
||||
update-languages:
|
||||
if: |
|
||||
github.repository == 'anuraghazra/github-readme-stats' ||
|
||||
github.repository == 'stats-organization/github-readme-stats' ||
|
||||
github.repository == 'stats-organization/github-stats-extended'
|
||||
name: Update supported languages
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [22.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
CI: true
|
||||
- name: Install Dependencies
|
||||
uses: ./.github/actions/install-dependencies
|
||||
|
||||
- name: Run update-languages-json.js script
|
||||
run: npm run generate-langs-json
|
||||
run: pnpm --filter github-readme-stats run generate-langs-json
|
||||
|
||||
- name: Create Pull Request if upstream language file is changed
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
commit-message: "refactor: update languages JSON"
|
||||
commit-message: "feat(backend): 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)
|
||||
body: "The
|
||||
[update-langs](https://github.com/stats-organization/github-stats-extended/actions/workflows/update-langs.yml)
|
||||
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"
|
||||
labels: "ci"
|
||||
+40
-9
@@ -1,12 +1,36 @@
|
||||
backend/.vercel
|
||||
backend/.env
|
||||
backend/node_modules
|
||||
backend/*.lock
|
||||
backend/coverage
|
||||
backend/benchmarks
|
||||
backend/vercel_token
|
||||
frontend/frontend/.env
|
||||
frontend/frontend/src/backend
|
||||
node_modules
|
||||
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Project
|
||||
coverage
|
||||
|
||||
apps/backend/.vercel
|
||||
apps/backend/.env
|
||||
apps/backend/node_modules
|
||||
apps/backend/*.lock
|
||||
apps/backend/coverage
|
||||
apps/backend/benchmarks
|
||||
apps/backend/vercel_token
|
||||
apps/backend-copy
|
||||
|
||||
apps/frontend/.env
|
||||
apps/frontend/src/backend
|
||||
apps/frontend/build
|
||||
|
||||
build-ts
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
@@ -14,3 +38,10 @@ frontend/frontend/src/backend
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.json
|
||||
*.code-workspace
|
||||
|
||||
# Playwright
|
||||
test-results/
|
||||
playwright-report/
|
||||
blob-report/
|
||||
playwright/.cache/
|
||||
playwright/.auth/
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
pnpm lint-staged
|
||||
pnpm run lint
|
||||
# TODO enable
|
||||
# npm test
|
||||
@@ -0,0 +1,5 @@
|
||||
pnpm-lock.yaml
|
||||
|
||||
# https://github.com/stats-organization/github-stats-extended/pull/26#discussion_r2709033732
|
||||
# Avoid prettier format on md files to simplify merge on upstream repo
|
||||
*.md
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": false,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.jsonc"],
|
||||
"options": {
|
||||
"trailingComma": "none"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"yzhang.markdown-all-in-one",
|
||||
"prettier.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"github.vscode-github-actions"
|
||||
]
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"markdown.extension.toc.levels": "1..3",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[javascript]": {
|
||||
"editor.tabSize": 2,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"cSpell.words": ["Wakatime"]
|
||||
}
|
||||
@@ -38,7 +38,7 @@ As more comfortable alternative, use the [GitHub-Stats-Extended Wizard](https://
|
||||
|
||||
- ...and development time:
|
||||
|
||||
[](https://wakatime.com/@ffflabs)
|
||||
[](https://wakatime.com/@alan)
|
||||
|
||||
- Pin more than 6 repos in your GitHub profile:
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "GitHub Readme Stats Dev",
|
||||
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": { "version": "22" }
|
||||
},
|
||||
"forwardPorts": [3000],
|
||||
"portsAttributes": {
|
||||
"3000": { "label": "HTTP" }
|
||||
},
|
||||
"appPort": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "npm install -g vercel",
|
||||
|
||||
// Use 'postStartCommand' to run commands after the container is started.
|
||||
"postStartCommand": "hostname dev && npm install",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"yzhang.markdown-all-in-one",
|
||||
"esbenp.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"github.vscode-github-actions"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"remoteUser": "root",
|
||||
"privileged": true
|
||||
}
|
||||
Vendored
+1
-1
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
@@ -1,7 +1,5 @@
|
||||
.devcontainer
|
||||
.github
|
||||
.husky
|
||||
.vscode
|
||||
benchmarks
|
||||
coverage
|
||||
scripts
|
||||
@@ -10,6 +8,4 @@ tests
|
||||
**/*.md
|
||||
**/*.svg
|
||||
.eslintrc.json
|
||||
.prettierignore
|
||||
.pretterrc.json
|
||||
codecov.yml
|
||||
+8
-7
@@ -1,16 +1,17 @@
|
||||
import { default as api } from "./api-renamed/index.js";
|
||||
/* eslint-disable import-x/no-unresolved */
|
||||
import { default as authenticate } from "./api-renamed/authenticate.js";
|
||||
import { default as deleteUser } from "./api-renamed/delete-user.js";
|
||||
import { default as downgrade } from "./api-renamed/downgrade.js";
|
||||
import { default as gist } from "./api-renamed/gist.js";
|
||||
import { default as api } from "./api-renamed/index.js";
|
||||
import { default as pin } from "./api-renamed/pin.js";
|
||||
import { default as topLangs } from "./api-renamed/top-langs.js";
|
||||
import { default as wakatime } from "./api-renamed/wakatime.js";
|
||||
import { default as wakatimeProxy } from "./api-renamed/wakatime-proxy.js";
|
||||
import { default as repeatRecent } from "./api-renamed/repeat-recent.js";
|
||||
import { default as patInfo } from "./api-renamed/status/pat-info.js";
|
||||
import { default as statusUp } from "./api-renamed/status/up.js";
|
||||
import { default as authenticate } from "./api-renamed/authenticate.js";
|
||||
import { default as deleteUser } from "./api-renamed/delete-user.js";
|
||||
import { default as topLangs } from "./api-renamed/top-langs.js";
|
||||
import { default as userAccess } from "./api-renamed/user-access.js";
|
||||
import { default as downgrade } from "./api-renamed/downgrade.js";
|
||||
import { default as wakatimeProxy } from "./api-renamed/wakatime-proxy.js";
|
||||
import { default as wakatime } from "./api-renamed/wakatime.js";
|
||||
|
||||
export default async (req, res) => {
|
||||
// remaining code expects express.js-like request and response objects
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
"expiration": 39600,
|
||||
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
|
||||
"passQuery": true
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
"expiration": 39600,
|
||||
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
|
||||
"passQuery": true
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
"expiration": 39600,
|
||||
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
|
||||
"passQuery": true
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
"expiration": 39600,
|
||||
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
|
||||
"passQuery": true
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
"expiration": 39600,
|
||||
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
|
||||
"passQuery": true
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { logger } from "../src/common/log.js";
|
||||
import { deleteUser } from "../src/common/database.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
|
||||
/**
|
||||
* @param {any} req The request.
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getUserAccessByKey, deleteUser } from "../src/common/database.js";
|
||||
import axios from "axios";
|
||||
|
||||
import { deleteUser, getUserAccessByKey } from "../src/common/database.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
|
||||
export default async (req, res) => {
|
||||
@@ -53,10 +54,10 @@ export default async (req, res) => {
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
res.statusCode = 500;
|
||||
res.send("Failed to delete GitHub authorization with private access");
|
||||
return;
|
||||
logger.error(err);
|
||||
res.statusCode = 500;
|
||||
res.send("Failed to delete GitHub authorization with private access");
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteUser(user_key);
|
||||
@@ -68,6 +69,9 @@ export default async (req, res) => {
|
||||
}).toString();
|
||||
|
||||
res.statusCode = 302;
|
||||
res.setHeader("Location", `https://github.com/login/oauth/authorize?${params}`);
|
||||
res.setHeader(
|
||||
"Location",
|
||||
`https://github.com/login/oauth/authorize?${params}`,
|
||||
);
|
||||
res.end();
|
||||
};
|
||||
@@ -1,22 +1,22 @@
|
||||
// @ts-check
|
||||
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
import { renderGistCard } from "../src/cards/gist.js";
|
||||
import { fetchGist } from "../src/fetchers/gist.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
import { guardAccess } from "../src/common/access.js";
|
||||
import {
|
||||
CACHE_TTL,
|
||||
resolveCacheSeconds,
|
||||
setCacheHeaders,
|
||||
setErrorCacheHeaders,
|
||||
} from "../src/common/cache.js";
|
||||
import { guardAccess } from "../src/common/access.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
import {
|
||||
MissingParamError,
|
||||
retrieveSecondaryMessage,
|
||||
} from "../src/common/error.js";
|
||||
import { parseBoolean } from "../src/common/ops.js";
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { fetchGist } from "../src/fetchers/gist.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
|
||||
// @ts-ignore
|
||||
export default async (req, res) => {
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
setCacheHeaders,
|
||||
setErrorCacheHeaders,
|
||||
} from "../src/common/cache.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
import {
|
||||
MissingParamError,
|
||||
retrieveSecondaryMessage,
|
||||
@@ -16,7 +17,6 @@ import { parseArray, parseBoolean } from "../src/common/ops.js";
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { fetchStats } from "../src/fetchers/stats.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
|
||||
// @ts-ignore
|
||||
export default async (req, res) => {
|
||||
@@ -87,26 +87,25 @@ export default async (req, res) => {
|
||||
);
|
||||
}
|
||||
|
||||
const safePattern = /^[-\w\/.,]+$/;
|
||||
const safePattern = /^[-\w/.,]+$/;
|
||||
if (
|
||||
(username && !safePattern.test(username)) ||
|
||||
(repo && !safePattern.test(repo)) ||
|
||||
(owner && !safePattern.test(owner))
|
||||
) {
|
||||
return res.send(
|
||||
renderError(
|
||||
{
|
||||
message: "Something went wrong",
|
||||
secondaryMessage: "Username, repository or owner contains unsafe characters",
|
||||
renderOptions: {
|
||||
title_color,
|
||||
text_color,
|
||||
bg_color,
|
||||
border_color,
|
||||
theme,
|
||||
},
|
||||
renderError({
|
||||
message: "Something went wrong",
|
||||
secondaryMessage:
|
||||
"Username, repository or owner contains unsafe characters",
|
||||
renderOptions: {
|
||||
title_color,
|
||||
text_color,
|
||||
bg_color,
|
||||
border_color,
|
||||
theme,
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +123,7 @@ export default async (req, res) => {
|
||||
parseBoolean(include_all_commits),
|
||||
parseArray(exclude_repo),
|
||||
showStats.includes("prs_merged") ||
|
||||
showStats.includes("prs_merged_percentage"),
|
||||
showStats.includes("prs_merged_percentage"),
|
||||
showStats.includes("discussions_started"),
|
||||
showStats.includes("discussions_answered"),
|
||||
parseInt(commits_year, 10),
|
||||
@@ -147,7 +146,9 @@ export default async (req, res) => {
|
||||
setCacheHeaders(res, cacheSeconds);
|
||||
|
||||
return res.send(
|
||||
renderStatsCard(stats, {
|
||||
renderStatsCard(
|
||||
stats,
|
||||
{
|
||||
hide: parseArray(hide),
|
||||
show_icons: parseBoolean(show_icons),
|
||||
hide_title: parseBoolean(hide_title),
|
||||
@@ -8,16 +8,15 @@ import {
|
||||
setCacheHeaders,
|
||||
setErrorCacheHeaders,
|
||||
} from "../src/common/cache.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
import {
|
||||
MissingParamError,
|
||||
retrieveSecondaryMessage,
|
||||
} from "../src/common/error.js";
|
||||
import { parseBoolean } from "../src/common/ops.js";
|
||||
import { parseArray } from "../src/common/ops.js";
|
||||
import { parseArray, parseBoolean } from "../src/common/ops.js";
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { fetchRepo } from "../src/fetchers/repo.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
|
||||
// @ts-ignore
|
||||
export default async (req, res) => {
|
||||
@@ -78,7 +77,7 @@ export default async (req, res) => {
|
||||
);
|
||||
}
|
||||
|
||||
const safePattern = /^[-\w\/.,]+$/;
|
||||
const safePattern = /^[-\w/.,]+$/;
|
||||
if (
|
||||
(username && !safePattern.test(username)) ||
|
||||
(repo && !safePattern.test(repo))
|
||||
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
|
||||
import { request } from "../../src/common/http.js";
|
||||
import retryer from "../../src/common/retryer.js";
|
||||
import { logger } from "../../src/common/log.js";
|
||||
import { default as retryer } from "../../src/common/retryer.js";
|
||||
|
||||
export const RATE_LIMIT_SECONDS = 60 * 3; // 1 request per 3 minutes
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
setCacheHeaders,
|
||||
setErrorCacheHeaders,
|
||||
} from "../src/common/cache.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
import {
|
||||
MissingParamError,
|
||||
retrieveSecondaryMessage,
|
||||
@@ -16,7 +17,6 @@ import { parseArray, parseBoolean } from "../src/common/ops.js";
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { fetchTopLanguages } from "../src/fetchers/top-languages.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
|
||||
// @ts-ignore
|
||||
export default async (req, res) => {
|
||||
@@ -29,6 +29,7 @@ export default async (req, res) => {
|
||||
title_color,
|
||||
text_color,
|
||||
bg_color,
|
||||
prog_bar_bg_color,
|
||||
theme,
|
||||
cache_seconds,
|
||||
layout,
|
||||
@@ -147,6 +148,7 @@ export default async (req, res) => {
|
||||
title_color,
|
||||
text_color,
|
||||
bg_color,
|
||||
prog_bar_bg_color,
|
||||
theme,
|
||||
layout,
|
||||
langs_count,
|
||||
@@ -1,5 +1,5 @@
|
||||
import { logger } from "../src/common/log.js";
|
||||
import { getUserAccessByKey } from "../src/common/database.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
|
||||
/**
|
||||
* @param {any} req The request.
|
||||
@@ -18,7 +18,7 @@ export default async (req, res) => {
|
||||
|
||||
res.send({
|
||||
privateAccess: result.privateAccess,
|
||||
token: result.token
|
||||
token: result.token,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime.js";
|
||||
|
||||
/**
|
||||
* @param {any} req The request.
|
||||
@@ -1,22 +1,22 @@
|
||||
// @ts-check
|
||||
|
||||
import { renderWakatimeCard } from "../src/cards/wakatime.js";
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
import { guardAccess } from "../src/common/access.js";
|
||||
import {
|
||||
CACHE_TTL,
|
||||
resolveCacheSeconds,
|
||||
setCacheHeaders,
|
||||
setErrorCacheHeaders,
|
||||
} from "../src/common/cache.js";
|
||||
import { guardAccess } from "../src/common/access.js";
|
||||
import { storeRequest } from "../src/common/database.js";
|
||||
import {
|
||||
MissingParamError,
|
||||
retrieveSecondaryMessage,
|
||||
} from "../src/common/error.js";
|
||||
import { parseArray, parseBoolean } from "../src/common/ops.js";
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
|
||||
// @ts-ignore
|
||||
export default async (req, res) => {
|
||||
@@ -1,11 +1,11 @@
|
||||
import "dotenv/config";
|
||||
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 gistCard from "./api/gist.js";
|
||||
import express from "express";
|
||||
|
||||
import gistCard from "./api-renamed/gist.js";
|
||||
import statsCard from "./api-renamed/index.js";
|
||||
import repoCard from "./api-renamed/pin.js";
|
||||
import langCard from "./api-renamed/top-langs.js";
|
||||
import wakatimeCard from "./api-renamed/wakatime.js";
|
||||
|
||||
const app = express();
|
||||
const router = express.Router();
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "github-readme-stats",
|
||||
"version": "1.0.0",
|
||||
"description": "Dynamically generate stats for your GitHub readme",
|
||||
"keywords": [
|
||||
"github-readme-stats",
|
||||
"readme-stats",
|
||||
"cards",
|
||||
"card-generator"
|
||||
],
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"homepage": "https://github-stats-extended.vercel.app/frontend",
|
||||
"bugs": {
|
||||
"url": "https://github.com/stats-organization/github-stats-extended/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/stats-organization/github-stats-extended.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"test:update:snapshot": "vitest -u",
|
||||
"test:e2e": "vitest --config vitest.config.e2e.ts",
|
||||
"theme-readme-gen": "node scripts/generate-theme-doc",
|
||||
"generate-langs-json": "node scripts/generate-langs-json",
|
||||
"bench": "vitest bench --run --config vitest.config.bench.ts"
|
||||
},
|
||||
"author": "Anurag Hazra",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@uppercod/css-to-object": "^1.1.1",
|
||||
"@vitest/coverage-v8": "catalog:default",
|
||||
"axios-mock-adapter": "^2.1.0",
|
||||
"express": "^5.2.1",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jsdom": "28.1.0",
|
||||
"vitest": "catalog:default"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.5",
|
||||
"emoji-name-map": "^2.0.3",
|
||||
"github-username-regex": "^1.0.0",
|
||||
"pg": "^8.18.0",
|
||||
"word-wrap": "^1.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "24.x"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
@@ -0,0 +1,30 @@
|
||||
import fs from "fs";
|
||||
|
||||
import axios from "axios";
|
||||
import { default as jsYaml } from "js-yaml";
|
||||
import * as prettier from "prettier";
|
||||
|
||||
const LANGS_FILEPATH = "./src/common/languageColors.json";
|
||||
|
||||
// Retrieve languages from github linguist repository yaml file
|
||||
const response = await axios.get(
|
||||
"https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml",
|
||||
);
|
||||
|
||||
// and convert them to a JS Object
|
||||
const languages = jsYaml.load(response.data);
|
||||
|
||||
const languageColors = {};
|
||||
|
||||
// Filter only language colors from the whole file
|
||||
Object.keys(languages).forEach((lang) => {
|
||||
languageColors[lang] = languages[lang].color;
|
||||
});
|
||||
|
||||
// Debug Print
|
||||
// console.dir(languageColors);
|
||||
const jsonString = JSON.stringify(languageColors);
|
||||
fs.writeFileSync(
|
||||
LANGS_FILEPATH,
|
||||
await prettier.format(jsonString, { parser: "json" }),
|
||||
);
|
||||
@@ -1,4 +1,5 @@
|
||||
import fs from "fs";
|
||||
|
||||
import { themes } from "../themes/index.js";
|
||||
|
||||
const TARGET_FILE = "./themes/README.md";
|
||||
@@ -84,4 +84,3 @@ function calculateRank({
|
||||
}
|
||||
|
||||
export { calculateRank };
|
||||
export default calculateRank;
|
||||
@@ -1,19 +1,18 @@
|
||||
// @ts-check
|
||||
|
||||
import {
|
||||
measureText,
|
||||
flexLayout,
|
||||
iconWithLabel,
|
||||
createLanguageNode,
|
||||
} from "../common/render.js";
|
||||
import Card from "../common/Card.js";
|
||||
import { default as Card } from "../common/Card.js";
|
||||
import { getCardColors } from "../common/color.js";
|
||||
import { kFormatter, wrapTextMultiline } from "../common/fmt.js";
|
||||
import { encodeHTML } from "../common/html.js";
|
||||
import { icons } from "../common/icons.js";
|
||||
import languageColors from "../common/languageColors.json" with { type: "json" };
|
||||
import { parseEmojis } from "../common/ops.js";
|
||||
|
||||
import {
|
||||
createLanguageNode,
|
||||
flexLayout,
|
||||
iconWithLabel,
|
||||
measureText,
|
||||
} from "../common/render.js";
|
||||
|
||||
const ICON_SIZE = 16;
|
||||
const CARD_DEFAULT_WIDTH = 400;
|
||||
@@ -139,5 +138,4 @@ const renderGistCard = (gistData, options = {}) => {
|
||||
`);
|
||||
};
|
||||
|
||||
export { renderGistCard, HEADER_MAX_LENGTH };
|
||||
export default renderGistCard;
|
||||
export { renderGistCard };
|
||||
@@ -1,20 +1,20 @@
|
||||
// @ts-check
|
||||
|
||||
import { Card } from "../common/Card.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { getCardColors } from "../common/color.js";
|
||||
import { kFormatter, wrapTextMultiline } from "../common/fmt.js";
|
||||
import { encodeHTML } from "../common/html.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { icons } from "../common/icons.js";
|
||||
import { clampValue, parseEmojis } from "../common/ops.js";
|
||||
import { buildSearchFilter } from "../common/ops.js";
|
||||
import { buildSearchFilter, clampValue, parseEmojis } from "../common/ops.js";
|
||||
import {
|
||||
flexLayout,
|
||||
measureText,
|
||||
iconWithLabel,
|
||||
createLanguageNode,
|
||||
flexLayout,
|
||||
iconWithLabel,
|
||||
measureText,
|
||||
} from "../common/render.js";
|
||||
import { repoCardLocales } from "../translations.js";
|
||||
|
||||
import { createTextNode } from "./stats.js";
|
||||
|
||||
const ICON_SIZE = 16;
|
||||
@@ -322,4 +322,3 @@ const renderRepoCard = (repo, options = {}) => {
|
||||
};
|
||||
|
||||
export { renderRepoCard };
|
||||
export default renderRepoCard;
|
||||
@@ -1,13 +1,12 @@
|
||||
// @ts-check
|
||||
|
||||
import { Card } from "../common/Card.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { getCardColors } from "../common/color.js";
|
||||
import { CustomError } from "../common/error.js";
|
||||
import { kFormatter } from "../common/fmt.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { icons, rankIcon } from "../common/icons.js";
|
||||
import { clampValue } from "../common/ops.js";
|
||||
import { buildSearchFilter } from "../common/ops.js";
|
||||
import { buildSearchFilter, clampValue } from "../common/ops.js";
|
||||
import { flexLayout, measureText } from "../common/render.js";
|
||||
import { statCardLocales, wakatimeCardLocales } from "../translations.js";
|
||||
|
||||
@@ -672,4 +671,3 @@ const renderStatsCard = (
|
||||
};
|
||||
|
||||
export { renderStatsCard, createTextNode };
|
||||
export default renderStatsCard;
|
||||
@@ -1,9 +1,9 @@
|
||||
// @ts-check
|
||||
|
||||
import { Card } from "../common/Card.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { getCardColors } from "../common/color.js";
|
||||
import { formatBytes } from "../common/fmt.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { chunkArray, clampValue, lowercaseTrim } from "../common/ops.js";
|
||||
import {
|
||||
createProgressNode,
|
||||
@@ -216,6 +216,7 @@ const getDisplayValue = (size, percentages, format) => {
|
||||
* @param {object} props Function properties.
|
||||
* @param {number} props.width The card width
|
||||
* @param {string} props.color Color of the programming language.
|
||||
* @param {string} props.progBarBgColor Color of the background of progress bar.
|
||||
* @param {string} props.name Name of the programming language.
|
||||
* @param {number} props.size Size of the programming language.
|
||||
* @param {number} props.totalSize Total size of all languages.
|
||||
@@ -226,6 +227,7 @@ const getDisplayValue = (size, percentages, format) => {
|
||||
const createProgressTextNode = ({
|
||||
width,
|
||||
color,
|
||||
progBarBgColor,
|
||||
name,
|
||||
size,
|
||||
totalSize,
|
||||
@@ -250,7 +252,9 @@ const createProgressTextNode = ({
|
||||
color,
|
||||
width: progressWidth,
|
||||
progress,
|
||||
progressBarBackgroundColor: "#ddd",
|
||||
progressBarBackgroundColor: progBarBgColor
|
||||
? `#${progBarBgColor}`
|
||||
: "#ddd",
|
||||
delay: staggerDelay + 300,
|
||||
})}
|
||||
</g>
|
||||
@@ -367,10 +371,17 @@ const createDonutLanguagesNode = ({ langs, totalSize, statsFormat }) => {
|
||||
* @param {Lang[]} langs Array of programming languages.
|
||||
* @param {number} width Card width.
|
||||
* @param {number} totalLanguageSize Total size of all languages.
|
||||
* @param progBarBgColor Color of the background of progress bar.
|
||||
* @param {string} statsFormat Stats format.
|
||||
* @returns {string} Normal layout card SVG object.
|
||||
*/
|
||||
const renderNormalLayout = (langs, width, totalLanguageSize, statsFormat) => {
|
||||
const renderNormalLayout = (
|
||||
langs,
|
||||
width,
|
||||
totalLanguageSize,
|
||||
progBarBgColor,
|
||||
statsFormat,
|
||||
) => {
|
||||
return flexLayout({
|
||||
items: langs.map((lang, index) => {
|
||||
return createProgressTextNode({
|
||||
@@ -381,6 +392,7 @@ const renderNormalLayout = (langs, width, totalLanguageSize, statsFormat) => {
|
||||
totalSize: totalLanguageSize,
|
||||
statsFormat,
|
||||
index,
|
||||
progBarBgColor,
|
||||
});
|
||||
}),
|
||||
gap: 40,
|
||||
@@ -787,6 +799,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
||||
title_color,
|
||||
text_color,
|
||||
bg_color,
|
||||
prog_bar_bg_color,
|
||||
hide,
|
||||
hide_progress,
|
||||
theme,
|
||||
@@ -872,6 +885,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
||||
langs,
|
||||
width,
|
||||
totalLanguageSize,
|
||||
prog_bar_bg_color,
|
||||
stats_format,
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
type ThemeNames = keyof typeof import("../../themes/index.js");
|
||||
type RankIcon = "default" | "github" | "percentile";
|
||||
|
||||
export type CommonOptions = {
|
||||
type CommonOptions = {
|
||||
title_color: string;
|
||||
icon_color: string;
|
||||
text_color: string;
|
||||
@@ -53,6 +53,7 @@ export type TopLangOptions = CommonOptions & {
|
||||
langs_count: number;
|
||||
disable_animations: boolean;
|
||||
hide_progress: boolean;
|
||||
prog_bar_bg_color: string;
|
||||
stats_format: "percentages" | "bytes";
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// @ts-check
|
||||
|
||||
import { Card } from "../common/Card.js";
|
||||
import { getCardColors } from "../common/color.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { getCardColors } from "../common/color.js";
|
||||
import languageColors from "../common/languageColors.json" with { type: "json" };
|
||||
import { clampValue, lowercaseTrim } from "../common/ops.js";
|
||||
import { createProgressNode, flexLayout } from "../common/render.js";
|
||||
import { wakatimeCardLocales } from "../translations.js";
|
||||
import languageColors from "../common/languageColors.json" with { type: "json" };
|
||||
|
||||
const DEFAULT_CARD_WIDTH = 495;
|
||||
const MIN_CARD_WIDTH = 250;
|
||||
@@ -378,7 +378,8 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
// @ts-ignore
|
||||
progressBarColor: titleColor,
|
||||
// @ts-ignore
|
||||
progressBarBackgroundColor: textColor === titleColor ? bgColor : textColor,
|
||||
progressBarBackgroundColor:
|
||||
textColor === titleColor ? bgColor : textColor,
|
||||
hideProgress: hide_progress,
|
||||
progressBarWidth: normalizedWidth - TOTAL_TEXT_WIDTH,
|
||||
});
|
||||
@@ -468,4 +469,3 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
};
|
||||
|
||||
export { renderWakatimeCard };
|
||||
export default renderWakatimeCard;
|
||||
@@ -40,4 +40,3 @@ class I18n {
|
||||
}
|
||||
|
||||
export { I18n };
|
||||
export default I18n;
|
||||
@@ -1,8 +1,8 @@
|
||||
// @ts-check
|
||||
|
||||
import { renderError } from "./render.js";
|
||||
import { blacklist } from "./blacklist.js";
|
||||
import { whitelist, gistWhitelist } from "./envs.js";
|
||||
import { gistWhitelist, whitelist } from "./envs.js";
|
||||
import { renderError } from "./render.js";
|
||||
|
||||
const NOT_WHITELISTED_USERNAME_MESSAGE = "This username is not whitelisted";
|
||||
const NOT_WHITELISTED_GIST_MESSAGE = "This gist ID is not whitelisted";
|
||||
@@ -7,4 +7,3 @@ const blacklist = [
|
||||
];
|
||||
|
||||
export { blacklist };
|
||||
export default blacklist;
|
||||
@@ -83,7 +83,8 @@ const getCardColors = ({
|
||||
theme,
|
||||
}) => {
|
||||
const defaultTheme = themes["default"];
|
||||
const isThemeProvided = theme !== null && theme !== undefined && theme in themes;
|
||||
const isThemeProvided =
|
||||
theme !== null && theme !== undefined && theme in themes;
|
||||
|
||||
// @ts-ignore
|
||||
const selectedTheme = isThemeProvided ? themes[theme] : defaultTheme;
|
||||
@@ -141,4 +142,4 @@ const getCardColors = ({
|
||||
return { titleColor, iconColor, textColor, bgColor, borderColor, ringColor };
|
||||
};
|
||||
|
||||
export { isValidHexColor, isValidGradient, getCardColors };
|
||||
export { getCardColors };
|
||||
@@ -0,0 +1,9 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* Valid owner affiliations for GitHub API queries.
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
const OWNER_AFFILIATIONS = ["OWNER", "COLLABORATOR", "ORGANIZATION_MEMBER"];
|
||||
|
||||
export { OWNER_AFFILIATIONS };
|
||||
@@ -1,5 +1,8 @@
|
||||
import pkg from "pg";
|
||||
const { Pool } = pkg;
|
||||
/**
|
||||
* In the browser this has to be mocked to avoid runtime errors
|
||||
* @see apps/frontend/vite.config.ts
|
||||
*/
|
||||
import { Pool } from "pg";
|
||||
|
||||
export const pool = process.env.POSTGRES_URL
|
||||
? new Pool({
|
||||
@@ -68,16 +71,16 @@ export async function storeRequest(req) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all requests older than 8 days from the database.
|
||||
* Deletes all requests older than the specified time from the database.
|
||||
*/
|
||||
export async function deleteOldRequests() {
|
||||
export async function deleteOldRequests(interval) {
|
||||
if (!pool) {
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteQuery = `
|
||||
DELETE FROM requests
|
||||
WHERE user_requested_at < NOW() - INTERVAL '8 days'
|
||||
WHERE user_requested_at < NOW() - INTERVAL '${interval}'
|
||||
`;
|
||||
try {
|
||||
let result = await pool.query(deleteQuery);
|
||||
@@ -92,11 +95,11 @@ export async function deleteOldRequests() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all requests which are between 11 hours and 8 days old.
|
||||
* Fetches all requests which are between minInterval and maxInterval old.
|
||||
*
|
||||
* @returns {Promise<string[]>} Array of all requests between 11 hours and 8 days old.
|
||||
* @returns {Promise<string[]>} Array of all requests between minInterval and maxInterval old.
|
||||
*/
|
||||
export async function getRecentRequests() {
|
||||
export async function getRecentRequests(minInterval, maxInterval) {
|
||||
if (!pool) {
|
||||
return [];
|
||||
}
|
||||
@@ -104,8 +107,8 @@ export async function getRecentRequests() {
|
||||
const query = `
|
||||
SELECT request
|
||||
FROM requests
|
||||
WHERE requested_at >= NOW() - INTERVAL '8 days'
|
||||
AND requested_at < NOW() - INTERVAL '11 hours'
|
||||
WHERE requested_at >= NOW() - INTERVAL '${maxInterval}'
|
||||
AND requested_at < NOW() - INTERVAL '${minInterval}'
|
||||
ORDER BY requested_at ASC
|
||||
`;
|
||||
let rows;
|
||||
@@ -215,7 +218,7 @@ export async function getUserAccessByKey(userKey) {
|
||||
}
|
||||
return {
|
||||
token: rows[0].access_token,
|
||||
privateAccess: rows[0].private_access
|
||||
privateAccess: rows[0].private_access,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code === "42P01") {
|
||||
@@ -250,7 +253,7 @@ export async function getUserAccessByName(userName) {
|
||||
}
|
||||
return {
|
||||
token: rows[0].access_token,
|
||||
privateAccess: rows[0].private_access
|
||||
privateAccess: rows[0].private_access,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code === "42P01") {
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-check
|
||||
|
||||
import { OWNER_AFFILIATIONS } from "./ops.js";
|
||||
import { OWNER_AFFILIATIONS } from "./constants.js";
|
||||
|
||||
/**
|
||||
* @type {string} A general message to ask user to try again later.
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-check
|
||||
|
||||
import wrap from "word-wrap";
|
||||
|
||||
import { encodeHTML } from "./html.js";
|
||||
|
||||
/**
|
||||
@@ -9,11 +9,14 @@
|
||||
* @returns {string} Encoded string.
|
||||
*/
|
||||
const encodeHTML = (str) => {
|
||||
return str
|
||||
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
||||
return "&#" + i.charCodeAt(0) + ";";
|
||||
})
|
||||
.replace(/\u0008/gim, "");
|
||||
return (
|
||||
str
|
||||
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
||||
return "&#" + i.charCodeAt(0) + ";";
|
||||
})
|
||||
// eslint-disable-next-line no-control-regex
|
||||
.replace(/\u0008/gim, "")
|
||||
);
|
||||
};
|
||||
|
||||
export { encodeHTML };
|
||||
@@ -53,4 +53,3 @@ const rankIcon = (rankIcon, rankLevel, percentile) => {
|
||||
};
|
||||
|
||||
export { icons, rankIcon };
|
||||
export default icons;
|
||||
@@ -0,0 +1,663 @@
|
||||
{
|
||||
"1C Enterprise": "#814CCC",
|
||||
"2-Dimensional Array": "#38761D",
|
||||
"4D": "#004289",
|
||||
"ABAP": "#E8274B",
|
||||
"ABAP CDS": "#555e25",
|
||||
"AGS Script": "#B9D9FF",
|
||||
"AIDL": "#34EB6B",
|
||||
"AL": "#3AA2B5",
|
||||
"ALGOL": "#D1E0DB",
|
||||
"AMPL": "#E6EFBB",
|
||||
"ANTLR": "#9DC3FF",
|
||||
"API Blueprint": "#2ACCA8",
|
||||
"APL": "#5A8164",
|
||||
"ASP.NET": "#9400ff",
|
||||
"ATS": "#1ac620",
|
||||
"ActionScript": "#882B0F",
|
||||
"Ada": "#02f88c",
|
||||
"Adblock Filter List": "#800000",
|
||||
"Adobe Font Metrics": "#fa0f00",
|
||||
"Agda": "#315665",
|
||||
"Aiken": "#640ff8",
|
||||
"Alloy": "#64C800",
|
||||
"Alpine Abuild": "#0D597F",
|
||||
"Altium Designer": "#A89663",
|
||||
"AngelScript": "#C7D7DC",
|
||||
"Answer Set Programming": "#A9CC29",
|
||||
"Ant Build System": "#A9157E",
|
||||
"Antlers": "#ff269e",
|
||||
"ApacheConf": "#d12127",
|
||||
"Apex": "#1797c0",
|
||||
"Apollo Guidance Computer": "#0B3D91",
|
||||
"AppleScript": "#101F1F",
|
||||
"Arc": "#aa2afe",
|
||||
"AsciiDoc": "#73a0c5",
|
||||
"AspectJ": "#a957b0",
|
||||
"Assembly": "#6E4C13",
|
||||
"Astro": "#ff5a03",
|
||||
"Asymptote": "#ff0000",
|
||||
"Augeas": "#9CC134",
|
||||
"AutoHotkey": "#6594b9",
|
||||
"AutoIt": "#1C3552",
|
||||
"Avro IDL": "#0040FF",
|
||||
"Awk": "#c30e9b",
|
||||
"B (Formal Method)": "#8aa8c5",
|
||||
"B4X": "#00e4ff",
|
||||
"BASIC": "#ff0000",
|
||||
"BQN": "#2b7067",
|
||||
"Ballerina": "#FF5000",
|
||||
"Batchfile": "#C1F12E",
|
||||
"Beef": "#a52f4e",
|
||||
"Berry": "#15A13C",
|
||||
"BibTeX": "#778899",
|
||||
"Bicep": "#519aba",
|
||||
"Bikeshed": "#5562ac",
|
||||
"Bison": "#6A463F",
|
||||
"BitBake": "#00bce4",
|
||||
"Blade": "#f7523f",
|
||||
"BlitzBasic": "#00FFAE",
|
||||
"BlitzMax": "#cd6400",
|
||||
"Bluespec": "#12223c",
|
||||
"Bluespec BH": "#12223c",
|
||||
"Boo": "#d4bec1",
|
||||
"Boogie": "#c80fa0",
|
||||
"Brainfuck": "#2F2530",
|
||||
"BrighterScript": "#66AABB",
|
||||
"Brightscript": "#662D91",
|
||||
"Browserslist": "#ffd539",
|
||||
"Bru": "#F4AA41",
|
||||
"BuildStream": "#006bff",
|
||||
"C": "#555555",
|
||||
"C#": "#178600",
|
||||
"C++": "#f34b7d",
|
||||
"C3": "#2563eb",
|
||||
"CAP CDS": "#0092d1",
|
||||
"CLIPS": "#00A300",
|
||||
"CMake": "#DA3434",
|
||||
"COLLADA": "#F1A42B",
|
||||
"CQL": "#006091",
|
||||
"CSON": "#244776",
|
||||
"CSS": "#663399",
|
||||
"CSV": "#237346",
|
||||
"CUE": "#5886E1",
|
||||
"CWeb": "#00007a",
|
||||
"Cabal Config": "#483465",
|
||||
"Caddyfile": "#22b638",
|
||||
"Cadence": "#00ef8b",
|
||||
"Cairo": "#ff4a48",
|
||||
"Cairo Zero": "#ff4a48",
|
||||
"CameLIGO": "#3be133",
|
||||
"Cangjie": "#00868B",
|
||||
"Cap'n Proto": "#c42727",
|
||||
"Carbon": "#222222",
|
||||
"Ceylon": "#dfa535",
|
||||
"Chapel": "#8dc63f",
|
||||
"ChucK": "#3f8000",
|
||||
"Circom": "#707575",
|
||||
"Cirru": "#ccccff",
|
||||
"Clarion": "#db901e",
|
||||
"Clarity": "#5546ff",
|
||||
"Classic ASP": "#6a40fd",
|
||||
"Clean": "#3F85AF",
|
||||
"Click": "#E4E6F3",
|
||||
"Clojure": "#db5855",
|
||||
"Closure Templates": "#0d948f",
|
||||
"Cloud Firestore Security Rules": "#FFA000",
|
||||
"Clue": "#0009b5",
|
||||
"CodeQL": "#140f46",
|
||||
"CoffeeScript": "#244776",
|
||||
"ColdFusion": "#ed2cd6",
|
||||
"ColdFusion CFC": "#ed2cd6",
|
||||
"Common Lisp": "#3fb68b",
|
||||
"Common Workflow Language": "#B5314C",
|
||||
"Component Pascal": "#B0CE4E",
|
||||
"Cooklang": "#E15A29",
|
||||
"Crystal": "#000100",
|
||||
"Csound": "#1a1a1a",
|
||||
"Csound Document": "#1a1a1a",
|
||||
"Csound Score": "#1a1a1a",
|
||||
"Cuda": "#3A4E3A",
|
||||
"Curry": "#531242",
|
||||
"Cylc": "#00b3fd",
|
||||
"Cypher": "#34c0eb",
|
||||
"Cython": "#fedf5b",
|
||||
"D": "#ba595e",
|
||||
"D2": "#526ee8",
|
||||
"DM": "#447265",
|
||||
"Dafny": "#FFEC25",
|
||||
"Darcs Patch": "#8eff23",
|
||||
"Dart": "#00B4AB",
|
||||
"Daslang": "#d3d3d3",
|
||||
"DataWeave": "#003a52",
|
||||
"Debian Package Control File": "#D70751",
|
||||
"DenizenScript": "#FBEE96",
|
||||
"Dhall": "#dfafff",
|
||||
"DirectX 3D File": "#aace60",
|
||||
"Dockerfile": "#384d54",
|
||||
"Dogescript": "#cca760",
|
||||
"Dotenv": "#e5d559",
|
||||
"Dune": "#89421e",
|
||||
"Dylan": "#6c616e",
|
||||
"E": "#ccce35",
|
||||
"ECL": "#8a1267",
|
||||
"ECLiPSe": "#001d9d",
|
||||
"EJS": "#a91e50",
|
||||
"EQ": "#a78649",
|
||||
"Earthly": "#2af0ff",
|
||||
"Easybuild": "#069406",
|
||||
"Ecere Projects": "#913960",
|
||||
"Ecmarkup": "#eb8131",
|
||||
"Edge": "#0dffe0",
|
||||
"EdgeQL": "#31A7FF",
|
||||
"EditorConfig": "#fff1f2",
|
||||
"Eiffel": "#4d6977",
|
||||
"Elixir": "#6e4a7e",
|
||||
"Elm": "#60B5CC",
|
||||
"Elvish": "#55BB55",
|
||||
"Elvish Transcript": "#55BB55",
|
||||
"Emacs Lisp": "#c065db",
|
||||
"EmberScript": "#FFF4F3",
|
||||
"Erlang": "#B83998",
|
||||
"Euphoria": "#FF790B",
|
||||
"F#": "#b845fc",
|
||||
"F*": "#572e30",
|
||||
"FIGlet Font": "#FFDDBB",
|
||||
"FIRRTL": "#2f632f",
|
||||
"FLUX": "#88ccff",
|
||||
"Factor": "#636746",
|
||||
"Fancy": "#7b9db4",
|
||||
"Fantom": "#14253c",
|
||||
"Faust": "#c37240",
|
||||
"Fennel": "#fff3d7",
|
||||
"Filebench WML": "#F6B900",
|
||||
"Flix": "#d44a45",
|
||||
"Fluent": "#ffcc33",
|
||||
"Forth": "#341708",
|
||||
"Fortran": "#4d41b1",
|
||||
"Fortran Free Form": "#4d41b1",
|
||||
"FreeBASIC": "#141AC9",
|
||||
"FreeMarker": "#0050b2",
|
||||
"Frege": "#00cafe",
|
||||
"Futhark": "#5f021f",
|
||||
"G-code": "#D08CF2",
|
||||
"GAML": "#FFC766",
|
||||
"GAMS": "#f49a22",
|
||||
"GAP": "#0000cc",
|
||||
"GCC Machine Description": "#FFCFAB",
|
||||
"GDScript": "#355570",
|
||||
"GDShader": "#478CBF",
|
||||
"GEDCOM": "#003058",
|
||||
"GLSL": "#5686a5",
|
||||
"GSC": "#FF6800",
|
||||
"Game Maker Language": "#71b417",
|
||||
"Gemfile.lock": "#701516",
|
||||
"Gemini": "#ff6900",
|
||||
"Genero 4gl": "#63408e",
|
||||
"Genero per": "#d8df39",
|
||||
"Genie": "#fb855d",
|
||||
"Genshi": "#951531",
|
||||
"Gentoo Ebuild": "#9400ff",
|
||||
"Gentoo Eclass": "#9400ff",
|
||||
"Gerber Image": "#d20b00",
|
||||
"Gherkin": "#5B2063",
|
||||
"Git Attributes": "#F44D27",
|
||||
"Git Commit": "#F44D27",
|
||||
"Git Config": "#F44D27",
|
||||
"Git Revision List": "#F44D27",
|
||||
"Gleam": "#ffaff3",
|
||||
"Glimmer JS": "#F5835F",
|
||||
"Glimmer TS": "#3178c6",
|
||||
"Glyph": "#c1ac7f",
|
||||
"Gnuplot": "#f0a9f0",
|
||||
"Go": "#00ADD8",
|
||||
"Go Checksums": "#00ADD8",
|
||||
"Go Module": "#00ADD8",
|
||||
"Go Template": "#00ADD8",
|
||||
"Go Workspace": "#00ADD8",
|
||||
"Godot Resource": "#355570",
|
||||
"Golo": "#88562A",
|
||||
"Gosu": "#82937f",
|
||||
"Grace": "#615f8b",
|
||||
"Gradle": "#02303a",
|
||||
"Gradle Kotlin DSL": "#02303a",
|
||||
"Grammatical Framework": "#ff0000",
|
||||
"GraphQL": "#e10098",
|
||||
"Graphviz (DOT)": "#2596be",
|
||||
"Groovy": "#4298b8",
|
||||
"Groovy Server Pages": "#4298b8",
|
||||
"HAProxy": "#106da9",
|
||||
"HCL": "#844FBA",
|
||||
"HIP": "#4F3A4F",
|
||||
"HLSL": "#aace60",
|
||||
"HOCON": "#9ff8ee",
|
||||
"HTML": "#e34c26",
|
||||
"HTML+ECR": "#2e1052",
|
||||
"HTML+EEX": "#6e4a7e",
|
||||
"HTML+ERB": "#701516",
|
||||
"HTML+PHP": "#4f5d95",
|
||||
"HTML+Razor": "#512be4",
|
||||
"HTTP": "#005C9C",
|
||||
"HXML": "#f68712",
|
||||
"Hack": "#878787",
|
||||
"Haml": "#ece2a9",
|
||||
"Handlebars": "#f7931e",
|
||||
"Harbour": "#0e60e3",
|
||||
"Hare": "#9d7424",
|
||||
"Haskell": "#5e5086",
|
||||
"Haxe": "#df7900",
|
||||
"HiveQL": "#dce200",
|
||||
"HolyC": "#ffefaf",
|
||||
"Hosts File": "#308888",
|
||||
"Hurl": "#FF0288",
|
||||
"Hy": "#7790B2",
|
||||
"IDL": "#a3522f",
|
||||
"IGOR Pro": "#0000cc",
|
||||
"INI": "#d1dbe0",
|
||||
"ISPC": "#2D68B1",
|
||||
"Idris": "#b30000",
|
||||
"Ignore List": "#000000",
|
||||
"ImageJ Macro": "#99AAFF",
|
||||
"Imba": "#16cec6",
|
||||
"Inno Setup": "#264b99",
|
||||
"Io": "#a9188d",
|
||||
"Ioke": "#078193",
|
||||
"Isabelle": "#FEFE00",
|
||||
"Isabelle ROOT": "#FEFE00",
|
||||
"J": "#9EEDFF",
|
||||
"JAR Manifest": "#b07219",
|
||||
"JCL": "#d90e09",
|
||||
"JFlex": "#DBCA00",
|
||||
"JSON": "#292929",
|
||||
"JSON with Comments": "#292929",
|
||||
"JSON5": "#267CB9",
|
||||
"JSONLD": "#0c479c",
|
||||
"JSONiq": "#40d47e",
|
||||
"Jac": "#FC792D",
|
||||
"Jai": "#ab8b4b",
|
||||
"Janet": "#0886a5",
|
||||
"Jasmin": "#d03600",
|
||||
"Java": "#b07219",
|
||||
"Java Properties": "#2A6277",
|
||||
"Java Server Pages": "#2A6277",
|
||||
"Java Template Engine": "#2A6277",
|
||||
"JavaScript": "#f1e05a",
|
||||
"JavaScript+ERB": "#f1e05a",
|
||||
"Jest Snapshot": "#15c213",
|
||||
"JetBrains MPS": "#21D789",
|
||||
"Jinja": "#a52a22",
|
||||
"Jison": "#56b3cb",
|
||||
"Jison Lex": "#56b3cb",
|
||||
"Jolie": "#843179",
|
||||
"Jsonnet": "#0064bd",
|
||||
"Julia": "#a270ba",
|
||||
"Julia REPL": "#a270ba",
|
||||
"Jupyter Notebook": "#DA5B0B",
|
||||
"Just": "#384d54",
|
||||
"KCL": "#7ABABF",
|
||||
"KDL": "#ffb3b3",
|
||||
"KFramework": "#4195c5",
|
||||
"KRL": "#28430A",
|
||||
"Kaitai Struct": "#773b37",
|
||||
"KakouneScript": "#6f8042",
|
||||
"KerboScript": "#41adf0",
|
||||
"KiCad Layout": "#2f4aab",
|
||||
"KiCad Legacy Layout": "#2f4aab",
|
||||
"KiCad Schematic": "#2f4aab",
|
||||
"KoLmafia ASH": "#B9D9B9",
|
||||
"Koka": "#215166",
|
||||
"Kotlin": "#A97BFF",
|
||||
"LFE": "#4C3023",
|
||||
"LLVM": "#185619",
|
||||
"LOLCODE": "#cc9900",
|
||||
"LSL": "#3d9970",
|
||||
"LabVIEW": "#fede06",
|
||||
"Lambdapi": "#8027a3",
|
||||
"Langium": "#2c8c87",
|
||||
"Lark": "#2980B9",
|
||||
"Lasso": "#999999",
|
||||
"Latte": "#f2a542",
|
||||
"Leo": "#C4FFC2",
|
||||
"Less": "#1d365d",
|
||||
"Lex": "#DBCA00",
|
||||
"LigoLANG": "#0e74ff",
|
||||
"LilyPond": "#9ccc7c",
|
||||
"Liquid": "#67b8de",
|
||||
"Literate Agda": "#315665",
|
||||
"Literate CoffeeScript": "#244776",
|
||||
"Literate Haskell": "#5e5086",
|
||||
"LiveCode Script": "#0c5ba5",
|
||||
"LiveScript": "#499886",
|
||||
"Logtalk": "#295b9a",
|
||||
"LookML": "#652B81",
|
||||
"Lua": "#000080",
|
||||
"Luau": "#00A2FF",
|
||||
"M3U": "#179C7D",
|
||||
"MATLAB": "#e16737",
|
||||
"MAXScript": "#00a6a6",
|
||||
"MDX": "#fcb32c",
|
||||
"MLIR": "#5EC8DB",
|
||||
"MQL4": "#62A8D6",
|
||||
"MQL5": "#4A76B8",
|
||||
"MTML": "#b7e1f4",
|
||||
"Macaulay2": "#d8ffff",
|
||||
"Makefile": "#427819",
|
||||
"Mako": "#7e858d",
|
||||
"Markdown": "#083fa1",
|
||||
"Marko": "#42bff2",
|
||||
"Mask": "#f97732",
|
||||
"Mathematical Programming System": "#0530ad",
|
||||
"Max": "#c4a79c",
|
||||
"Mercury": "#ff2b2b",
|
||||
"Mermaid": "#ff3670",
|
||||
"Meson": "#007800",
|
||||
"Metal": "#8f14e9",
|
||||
"MiniYAML": "#ff1111",
|
||||
"MiniZinc": "#06a9e6",
|
||||
"Mint": "#02b046",
|
||||
"Mirah": "#c7a938",
|
||||
"Modelica": "#de1d31",
|
||||
"Modula-2": "#10253f",
|
||||
"Modula-3": "#223388",
|
||||
"Mojo": "#ff4c1f",
|
||||
"Monkey C": "#8D6747",
|
||||
"MoonBit": "#b92381",
|
||||
"MoonScript": "#ff4585",
|
||||
"Motoko": "#fbb03b",
|
||||
"Motorola 68K Assembly": "#005daa",
|
||||
"Move": "#4a137a",
|
||||
"Mustache": "#724b3b",
|
||||
"NCL": "#28431f",
|
||||
"NMODL": "#00356B",
|
||||
"NPM Config": "#cb3837",
|
||||
"NWScript": "#111522",
|
||||
"Nasal": "#1d2c4e",
|
||||
"Nearley": "#990000",
|
||||
"Nemerle": "#3d3c6e",
|
||||
"NetLinx": "#0aa0ff",
|
||||
"NetLinx+ERB": "#747faa",
|
||||
"NetLogo": "#ff6375",
|
||||
"NewLisp": "#87AED7",
|
||||
"Nextflow": "#3ac486",
|
||||
"Nginx": "#009639",
|
||||
"Nickel": "#E0C3FC",
|
||||
"Nim": "#ffc200",
|
||||
"Nit": "#009917",
|
||||
"Nix": "#7e7eff",
|
||||
"Noir": "#2f1f49",
|
||||
"Nu": "#c9df40",
|
||||
"NumPy": "#9C8AF9",
|
||||
"Nunjucks": "#3d8137",
|
||||
"Nushell": "#4E9906",
|
||||
"OASv2-json": "#85ea2d",
|
||||
"OASv2-yaml": "#85ea2d",
|
||||
"OASv3-json": "#85ea2d",
|
||||
"OASv3-yaml": "#85ea2d",
|
||||
"OCaml": "#ef7a08",
|
||||
"OMNeT++ MSG": "#a0e0a0",
|
||||
"OMNeT++ NED": "#08607c",
|
||||
"ObjectScript": "#424893",
|
||||
"Objective-C": "#438eff",
|
||||
"Objective-C++": "#6866fb",
|
||||
"Objective-J": "#ff0c5a",
|
||||
"Odin": "#60AFFE",
|
||||
"Omgrofl": "#cabbff",
|
||||
"Opal": "#f7ede0",
|
||||
"Open Policy Agent": "#7d9199",
|
||||
"OpenAPI Specification v2": "#85ea2d",
|
||||
"OpenAPI Specification v3": "#85ea2d",
|
||||
"OpenCL": "#ed2e2d",
|
||||
"OpenEdge ABL": "#5ce600",
|
||||
"OpenQASM": "#AA70FF",
|
||||
"OpenSCAD": "#e5cd45",
|
||||
"Option List": "#476732",
|
||||
"Org": "#77aa99",
|
||||
"OverpassQL": "#cce2aa",
|
||||
"Oxygene": "#cdd0e3",
|
||||
"Oz": "#fab738",
|
||||
"P4": "#7055b5",
|
||||
"PDDL": "#0d00ff",
|
||||
"PEG.js": "#234d6b",
|
||||
"PHP": "#4F5D95",
|
||||
"PLSQL": "#dad8d8",
|
||||
"PLpgSQL": "#336790",
|
||||
"POV-Ray SDL": "#6bac65",
|
||||
"Pact": "#F7A8B8",
|
||||
"Pan": "#cc0000",
|
||||
"Papyrus": "#6600cc",
|
||||
"Parrot": "#f3ca0a",
|
||||
"Pascal": "#E3F171",
|
||||
"Pawn": "#dbb284",
|
||||
"Pep8": "#C76F5B",
|
||||
"Perl": "#0298c3",
|
||||
"PicoLisp": "#6067af",
|
||||
"PigLatin": "#fcd7de",
|
||||
"Pike": "#005390",
|
||||
"Pip Requirements": "#FFD343",
|
||||
"Pkl": "#6b9543",
|
||||
"PlantUML": "#fbbd16",
|
||||
"PogoScript": "#d80074",
|
||||
"Polar": "#ae81ff",
|
||||
"Portugol": "#f8bd00",
|
||||
"PostCSS": "#dc3a0c",
|
||||
"PostScript": "#da291c",
|
||||
"PowerBuilder": "#8f0f8d",
|
||||
"PowerShell": "#012456",
|
||||
"Praat": "#c8506d",
|
||||
"Prisma": "#0c344b",
|
||||
"Processing": "#0096D8",
|
||||
"Procfile": "#3B2F63",
|
||||
"Prolog": "#74283c",
|
||||
"Promela": "#de0000",
|
||||
"Propeller Spin": "#7fa2a7",
|
||||
"Pug": "#a86454",
|
||||
"Puppet": "#302B6D",
|
||||
"PureBasic": "#5a6986",
|
||||
"PureScript": "#1D222D",
|
||||
"Pyret": "#ee1e10",
|
||||
"Python": "#3572A5",
|
||||
"Python console": "#3572A5",
|
||||
"Python traceback": "#3572A5",
|
||||
"Q#": "#fed659",
|
||||
"QML": "#44a51c",
|
||||
"Qt Script": "#00b841",
|
||||
"Quake": "#882233",
|
||||
"QuakeC": "#975777",
|
||||
"QuickBASIC": "#008080",
|
||||
"R": "#198CE7",
|
||||
"RAML": "#77d9fb",
|
||||
"RAScript": "#2C97FA",
|
||||
"RBS": "#701516",
|
||||
"RDoc": "#701516",
|
||||
"REXX": "#d90e09",
|
||||
"RMarkdown": "#198ce7",
|
||||
"RON": "#a62c00",
|
||||
"ROS Interface": "#22314e",
|
||||
"RPGLE": "#2BDE21",
|
||||
"RUNOFF": "#665a4e",
|
||||
"Racket": "#3c5caa",
|
||||
"Ragel": "#9d5200",
|
||||
"Raku": "#0000fb",
|
||||
"Rascal": "#fffaa0",
|
||||
"ReScript": "#ed5051",
|
||||
"Reason": "#ff5847",
|
||||
"ReasonLIGO": "#ff5847",
|
||||
"Rebol": "#358a5b",
|
||||
"Record Jar": "#0673ba",
|
||||
"Red": "#f50000",
|
||||
"Regular Expression": "#009a00",
|
||||
"Ren'Py": "#ff7f7f",
|
||||
"Rez": "#FFDAB3",
|
||||
"Ring": "#2D54CB",
|
||||
"Riot": "#A71E49",
|
||||
"RobotFramework": "#00c0b5",
|
||||
"Roc": "#7c38f5",
|
||||
"Rocq Prover": "#d0b68c",
|
||||
"Roff": "#ecdebe",
|
||||
"Roff Manpage": "#ecdebe",
|
||||
"Rouge": "#cc0088",
|
||||
"RouterOS Script": "#DE3941",
|
||||
"Ruby": "#701516",
|
||||
"Rust": "#dea584",
|
||||
"SAS": "#B34936",
|
||||
"SCSS": "#c6538c",
|
||||
"SPARQL": "#0C4597",
|
||||
"SQF": "#3F3F3F",
|
||||
"SQL": "#e38c00",
|
||||
"SQLPL": "#e38c00",
|
||||
"SRecode Template": "#348a34",
|
||||
"STL": "#373b5e",
|
||||
"SVG": "#ff9900",
|
||||
"Sail": "#259dd5",
|
||||
"SaltStack": "#646464",
|
||||
"Sass": "#a53b70",
|
||||
"Scala": "#c22d40",
|
||||
"Scaml": "#bd181a",
|
||||
"Scenic": "#fdc700",
|
||||
"Scheme": "#1e4aec",
|
||||
"Scilab": "#ca0f21",
|
||||
"Self": "#0579aa",
|
||||
"ShaderLab": "#222c37",
|
||||
"Shell": "#89e051",
|
||||
"ShellCheck Config": "#cecfcb",
|
||||
"Shen": "#120F14",
|
||||
"Simple File Verification": "#C9BFED",
|
||||
"Singularity": "#64E6AD",
|
||||
"Slang": "#1fbec9",
|
||||
"Slash": "#007eff",
|
||||
"Slice": "#003fa2",
|
||||
"Slim": "#2b2b2b",
|
||||
"Slint": "#2379F4",
|
||||
"SmPL": "#c94949",
|
||||
"Smalltalk": "#596706",
|
||||
"Smarty": "#f0c040",
|
||||
"Smithy": "#c44536",
|
||||
"Snakemake": "#419179",
|
||||
"Solidity": "#AA6746",
|
||||
"SourcePawn": "#f69e1d",
|
||||
"Squirrel": "#800000",
|
||||
"Stan": "#b2011d",
|
||||
"Standard ML": "#dc566d",
|
||||
"Starlark": "#76d275",
|
||||
"Stata": "#1a5f91",
|
||||
"StringTemplate": "#3fb34f",
|
||||
"Stylus": "#ff6347",
|
||||
"SubRip Text": "#9e0101",
|
||||
"SugarSS": "#2fcc9f",
|
||||
"SuperCollider": "#46390b",
|
||||
"SurrealQL": "#ff00a0",
|
||||
"Survex data": "#ffcc99",
|
||||
"Svelte": "#ff3e00",
|
||||
"Sway": "#00F58C",
|
||||
"Sweave": "#198ce7",
|
||||
"Swift": "#F05138",
|
||||
"SystemVerilog": "#DAE1C2",
|
||||
"TI Program": "#A0AA87",
|
||||
"TL-Verilog": "#C40023",
|
||||
"TLA": "#4b0079",
|
||||
"TMDL": "#f0c913",
|
||||
"TOML": "#9c4221",
|
||||
"TSQL": "#e38c00",
|
||||
"TSV": "#237346",
|
||||
"TSX": "#3178c6",
|
||||
"TXL": "#0178b8",
|
||||
"Tact": "#48b5ff",
|
||||
"Talon": "#333333",
|
||||
"Tcl": "#e4cc98",
|
||||
"TeX": "#3D6117",
|
||||
"Teal": "#00B1BC",
|
||||
"Terra": "#00004c",
|
||||
"Terraform Template": "#7b42bb",
|
||||
"TextGrid": "#c8506d",
|
||||
"TextMate Properties": "#df66e4",
|
||||
"Textile": "#ffe7ac",
|
||||
"Thrift": "#D12127",
|
||||
"Toit": "#c2c9fb",
|
||||
"Tor Config": "#59316b",
|
||||
"Tree-sitter Query": "#8ea64c",
|
||||
"Turing": "#cf142b",
|
||||
"Twig": "#c1d026",
|
||||
"TypeScript": "#3178c6",
|
||||
"TypeSpec": "#4A3665",
|
||||
"Typst": "#239dad",
|
||||
"Unified Parallel C": "#4e3617",
|
||||
"Unity3D Asset": "#222c37",
|
||||
"Uno": "#9933cc",
|
||||
"UnrealScript": "#a54c4d",
|
||||
"Untyped Plutus Core": "#36adbd",
|
||||
"UrWeb": "#ccccee",
|
||||
"V": "#4f87c4",
|
||||
"VBA": "#867db1",
|
||||
"VBScript": "#15dcdc",
|
||||
"VCL": "#148AA8",
|
||||
"VHDL": "#adb2cb",
|
||||
"Vala": "#a56de2",
|
||||
"Valve Data Format": "#f26025",
|
||||
"Velocity Template Language": "#507cff",
|
||||
"Vento": "#ff0080",
|
||||
"Verilog": "#b2b7f8",
|
||||
"Vim Help File": "#199f4b",
|
||||
"Vim Script": "#199f4b",
|
||||
"Vim Snippet": "#199f4b",
|
||||
"Visual Basic .NET": "#945db7",
|
||||
"Visual Basic 6.0": "#2c6353",
|
||||
"Volt": "#1F1F1F",
|
||||
"Vue": "#41b883",
|
||||
"Vyper": "#9F4CF2",
|
||||
"WDL": "#42f1f4",
|
||||
"WGSL": "#1a5e9a",
|
||||
"Web Ontology Language": "#5b70bd",
|
||||
"WebAssembly": "#04133b",
|
||||
"WebAssembly Interface Type": "#6250e7",
|
||||
"Whiley": "#d5c397",
|
||||
"Wikitext": "#fc5757",
|
||||
"Windows Registry Entries": "#52d5ff",
|
||||
"Witcher Script": "#ff0000",
|
||||
"Wolfram Language": "#dd1100",
|
||||
"Wollok": "#a23738",
|
||||
"World of Warcraft Addon Data": "#f7e43f",
|
||||
"Wren": "#383838",
|
||||
"X10": "#4B6BEF",
|
||||
"XC": "#99DA07",
|
||||
"XML": "#0060ac",
|
||||
"XML Property List": "#0060ac",
|
||||
"XQuery": "#5232e7",
|
||||
"XSLT": "#EB8CEB",
|
||||
"Xmake": "#22a079",
|
||||
"Xojo": "#81bd41",
|
||||
"Xonsh": "#285EEF",
|
||||
"Xtend": "#24255d",
|
||||
"YAML": "#cb171e",
|
||||
"YARA": "#220000",
|
||||
"YASnippet": "#32AB90",
|
||||
"Yacc": "#4B6C4B",
|
||||
"Yul": "#794932",
|
||||
"ZAP": "#0d665e",
|
||||
"ZIL": "#dc75e5",
|
||||
"ZenScript": "#00BCD1",
|
||||
"Zephir": "#118f9e",
|
||||
"Zig": "#ec915c",
|
||||
"Zimpl": "#d67711",
|
||||
"Zmodel": "#ff7100",
|
||||
"crontab": "#ead7ac",
|
||||
"eC": "#913960",
|
||||
"fish": "#4aae47",
|
||||
"hoon": "#00b171",
|
||||
"iCalendar": "#ec564c",
|
||||
"jq": "#c7254e",
|
||||
"kvlang": "#1da6e0",
|
||||
"mIRC Script": "#3d57c3",
|
||||
"mcfunction": "#E22837",
|
||||
"mdsvex": "#5f9ea0",
|
||||
"mupad": "#244963",
|
||||
"nanorc": "#2d004d",
|
||||
"nesC": "#94B0C7",
|
||||
"ooc": "#b0b77e",
|
||||
"q": "#0040cd",
|
||||
"reStructuredText": "#141414",
|
||||
"sed": "#64b970",
|
||||
"templ": "#66D0DD",
|
||||
"vCard": "#ee2647",
|
||||
"wisp": "#7582D1",
|
||||
"xBase": "#403a40"
|
||||
}
|
||||
@@ -11,4 +11,3 @@ const logger =
|
||||
process.env.NODE_ENV === "test" ? { log: noop, error: noop } : console;
|
||||
|
||||
export { logger };
|
||||
export default logger;
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import toEmoji from "emoji-name-map";
|
||||
|
||||
const OWNER_AFFILIATIONS = ["OWNER", "COLLABORATOR", "ORGANIZATION_MEMBER"];
|
||||
import { OWNER_AFFILIATIONS } from "./constants.js";
|
||||
import { CustomError } from "./error.js";
|
||||
|
||||
/**
|
||||
* Returns boolean if value is either "true" or "false" else the value as it is.
|
||||
@@ -159,7 +160,6 @@ const buildSearchFilter = (repos = [], owners = []) => {
|
||||
};
|
||||
|
||||
export {
|
||||
OWNER_AFFILIATIONS,
|
||||
parseBoolean,
|
||||
parseArray,
|
||||
clampValue,
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-check
|
||||
|
||||
import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js";
|
||||
import { getCardColors } from "./color.js";
|
||||
import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js";
|
||||
import { encodeHTML } from "./html.js";
|
||||
import { clampValue } from "./ops.js";
|
||||
|
||||
@@ -72,7 +72,7 @@ const createProgressNode = ({
|
||||
|
||||
return `
|
||||
<svg width="${width}" x="${x}" y="${y}">
|
||||
<rect rx="5" ry="5" x="0" y="0" width="${width}" height="8" fill="${progressBarBackgroundColor}"></rect>
|
||||
<rect data-testid="progress-background" rx="5" ry="5" x="0" y="0" width="${width}" height="8" fill="${progressBarBackgroundColor}"></rect>
|
||||
<svg data-testid="lang-progress" width="${progressPercentage}%">
|
||||
<rect
|
||||
height="8"
|
||||
@@ -1,9 +1,19 @@
|
||||
// @ts-check
|
||||
|
||||
import { getUserAccessByName } from "./database.js";
|
||||
import { CustomError } from "./error.js";
|
||||
import { logger } from "./log.js";
|
||||
import { getUserAccessByKey, getUserAccessByName } from "./database.js";
|
||||
|
||||
/**
|
||||
* Returns a random integer from 0 (inclusive) to `max` (exclusive).
|
||||
*
|
||||
* The value is generated using `Math.random()` and uniformly distributed
|
||||
* across the range.
|
||||
*
|
||||
* @param {number} max The upper bound (exclusive). Must be a positive number.
|
||||
*
|
||||
* @returns {number} A random integer `n` such that `0 <= n < max`.
|
||||
*/
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
@@ -17,7 +27,7 @@ function getRandomInt(max) {
|
||||
* Try to execute the fetcher function until it succeeds or the max number of retries is reached.
|
||||
*
|
||||
* @param {FetcherFunction} fetcher The fetcher function.
|
||||
* @param username GitHub username of the user whose PAT to use, if available
|
||||
* @param {string?} username GitHub username of the user whose PAT to use, if available
|
||||
* @param {any} variables Object with arguments to pass to the fetcher function.
|
||||
* @returns {Promise<any>} The response from the fetcher function.
|
||||
*/
|
||||
@@ -36,14 +46,13 @@ const retryer = async (fetcher, username, variables) => {
|
||||
);
|
||||
PATs = patNames.map((name) => ({ name, value: process.env[name] }));
|
||||
}
|
||||
const RETRIES = process.env.NODE_ENV === "test" ? 7 : PATs.length;
|
||||
|
||||
if (!RETRIES) {
|
||||
if (!PATs.length) {
|
||||
throw new CustomError("No GitHub API tokens found", CustomError.NO_TOKENS);
|
||||
}
|
||||
const startPAT = getRandomInt(PATs.length);
|
||||
|
||||
for (let retries = 0; retries < RETRIES; retries++) {
|
||||
for (let retries = 0; retries < PATs.length; retries++) {
|
||||
const currentPAT = PATs[(startPAT + retries) % PATs.length];
|
||||
|
||||
try {
|
||||
@@ -1,8 +1,8 @@
|
||||
// @ts-check
|
||||
|
||||
import { retryer } from "../common/retryer.js";
|
||||
import { MissingParamError } from "../common/error.js";
|
||||
import { request } from "../common/http.js";
|
||||
import { retryer } from "../common/retryer.js";
|
||||
|
||||
const QUERY = `
|
||||
query gistInfo($gistName: String!) {
|
||||
@@ -111,4 +111,3 @@ const fetchGist = async (id) => {
|
||||
};
|
||||
|
||||
export { fetchGist };
|
||||
export default fetchGist;
|
||||
@@ -3,6 +3,7 @@
|
||||
import { MissingParamError } from "../common/error.js";
|
||||
import { request } from "../common/http.js";
|
||||
import { retryer } from "../common/retryer.js";
|
||||
|
||||
import { fetchRepoUserStats } from "./stats.js";
|
||||
|
||||
/**
|
||||
@@ -159,4 +160,3 @@ const fetchRepo = async (
|
||||
};
|
||||
|
||||
export { fetchRepo };
|
||||
export default fetchRepo;
|
||||
@@ -1,21 +1,16 @@
|
||||
// @ts-check
|
||||
|
||||
import axios from "axios";
|
||||
import * as dotenv from "dotenv";
|
||||
import githubUsernameRegex from "github-username-regex";
|
||||
|
||||
import { calculateRank } from "../calculateRank.js";
|
||||
import { retryer } from "../common/retryer.js";
|
||||
import {
|
||||
buildSearchFilter,
|
||||
parseOwnerAffiliations,
|
||||
} from "../common/ops.js";
|
||||
import { logger } from "../common/log.js";
|
||||
import { excludeRepositories } from "../common/envs.js";
|
||||
import { CustomError, MissingParamError } from "../common/error.js";
|
||||
import { wrapTextMultiline } from "../common/fmt.js";
|
||||
import { request } from "../common/http.js";
|
||||
|
||||
dotenv.config();
|
||||
import { logger } from "../common/log.js";
|
||||
import { buildSearchFilter, parseOwnerAffiliations } from "../common/ops.js";
|
||||
import { retryer } from "../common/retryer.js";
|
||||
|
||||
// GraphQL queries.
|
||||
const GRAPHQL_REPOS_FIELD = `
|
||||
@@ -164,11 +159,13 @@ const statsFetcher = async ({
|
||||
const repoNodesWithStars = repoNodes.filter(
|
||||
(node) => node.stargazers.totalCount !== 0,
|
||||
);
|
||||
|
||||
hasNextPage =
|
||||
(process.env.FETCH_MULTI_PAGE_STARS === "true" ||
|
||||
process.env.FETCH_MULTI_PAGE_STARS > fetchedPages) &&
|
||||
repoNodes.length === repoNodesWithStars.length &&
|
||||
res.data.data.user.repositories.pageInfo.hasNextPage;
|
||||
|
||||
endCursor = res.data.data.user.repositories.pageInfo.endCursor;
|
||||
}
|
||||
|
||||
@@ -191,10 +188,7 @@ const fetchTotalItems = (variables, token) => {
|
||||
`https://api.github.com/search/` +
|
||||
variables.type +
|
||||
`?per_page=1&q=` +
|
||||
buildSearchFilter(variables.repo, variables.owner).replaceAll(
|
||||
" ",
|
||||
"+",
|
||||
) +
|
||||
buildSearchFilter(variables.repo, variables.owner).replaceAll(" ", "+") +
|
||||
variables.filter,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -461,4 +455,3 @@ const fetchStats = async (
|
||||
};
|
||||
|
||||
export { fetchStats, fetchRepoUserStats };
|
||||
export default fetchStats;
|
||||
@@ -1,12 +1,12 @@
|
||||
// @ts-check
|
||||
|
||||
import { retryer } from "../common/retryer.js";
|
||||
import { parseOwnerAffiliations } from "../common/ops.js";
|
||||
import { logger } from "../common/log.js";
|
||||
import { excludeRepositories } from "../common/envs.js";
|
||||
import { CustomError, MissingParamError } from "../common/error.js";
|
||||
import { wrapTextMultiline } from "../common/fmt.js";
|
||||
import { request } from "../common/http.js";
|
||||
import { logger } from "../common/log.js";
|
||||
import { parseOwnerAffiliations } from "../common/ops.js";
|
||||
import { retryer } from "../common/retryer.js";
|
||||
|
||||
/**
|
||||
* Top languages fetcher object.
|
||||
@@ -73,7 +73,10 @@ const fetchTopLanguages = async (
|
||||
}
|
||||
ownerAffiliations = parseOwnerAffiliations(ownerAffiliations);
|
||||
|
||||
const res = await retryer(fetcher, username, { login: username, ownerAffiliations });
|
||||
const res = await retryer(fetcher, username, {
|
||||
login: username,
|
||||
ownerAffiliations,
|
||||
});
|
||||
|
||||
if (res.data.errors) {
|
||||
logger.error(res.data.errors);
|
||||
@@ -163,4 +166,3 @@ const fetchTopLanguages = async (
|
||||
};
|
||||
|
||||
export { fetchTopLanguages };
|
||||
export default fetchTopLanguages;
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-check
|
||||
|
||||
import axios from "axios";
|
||||
|
||||
import { CustomError, MissingParamError } from "../common/error.js";
|
||||
|
||||
/**
|
||||
@@ -34,4 +35,3 @@ const fetchWakatimeStats = async ({ username, api_domain }) => {
|
||||
};
|
||||
|
||||
export { fetchWakatimeStats };
|
||||
export default fetchWakatimeStats;
|
||||
@@ -1,8 +1,9 @@
|
||||
import axios from "axios";
|
||||
|
||||
import {
|
||||
pool,
|
||||
deleteOldRequests,
|
||||
getRecentRequests,
|
||||
pool,
|
||||
} from "./common/database.js";
|
||||
|
||||
/**
|
||||
@@ -47,16 +48,21 @@ async function makeRequests(urls, poolSize) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats requests made in the last 8 days, excluding those made in the last 11 hours.
|
||||
* Repeats requests made in the last 8 days (or configured interval), excluding those made in the last 11 hours (or configured interval).
|
||||
*/
|
||||
export async function repeatRecentRequests() {
|
||||
if (!pool) {
|
||||
console.error("Postgres pool is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteOldRequests();
|
||||
const urls = await getRecentRequests();
|
||||
let maxInterval = process.env.DELETE_AFTER_HOURS
|
||||
? `${process.env.DELETE_AFTER_HOURS} hours`
|
||||
: "8 days";
|
||||
let minInterval = process.env.UPDATE_AFTER_HOURS
|
||||
? `${process.env.UPDATE_AFTER_HOURS} hours`
|
||||
: "11 hours";
|
||||
await deleteOldRequests(maxInterval);
|
||||
const urls = await getRecentRequests(minInterval, maxInterval);
|
||||
if (urls.length === 0) {
|
||||
console.log("No recent requests found.");
|
||||
} else {
|
||||
@@ -62,6 +62,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: `GitHub статистика корисника ${encodedName}`,
|
||||
"sr-latn": `GitHub statistika korisnika ${encodedName}`,
|
||||
no: `GitHub-statistikk for ${encodedName}`,
|
||||
be: `Статыстыка GitHub карыстальніка ${encodedName}`,
|
||||
},
|
||||
"statcard.ranktitle": {
|
||||
en: `${encodedName}'${apostrophe} GitHub Rank`,
|
||||
@@ -110,6 +111,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: `Ранк корисника ${encodedName}`,
|
||||
"sr-latn": `Rank korisnika ${encodedName}`,
|
||||
no: `GitHub-statistikk for ${encodedName}`,
|
||||
be: `Рэйтынг GitHub карыстальніка ${encodedName}`,
|
||||
},
|
||||
"statcard.totalstars": {
|
||||
en: "Total Stars Earned",
|
||||
@@ -158,6 +160,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Број освојених звездица",
|
||||
"sr-latn": "Broj osvojenih zvezdica",
|
||||
no: "Totalt antall stjerner",
|
||||
be: "Зароблена зорак агулам",
|
||||
},
|
||||
"statcard.commits": {
|
||||
en: "Total Commits",
|
||||
@@ -206,6 +209,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Укупно commit-ова",
|
||||
"sr-latn": "Ukupno commit-ova",
|
||||
no: "Totalt antall commits",
|
||||
be: "Агульная колькасць камітаў",
|
||||
},
|
||||
"statcard.prs": {
|
||||
en: "Total PRs",
|
||||
@@ -254,6 +258,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Укупно PR-ова",
|
||||
"sr-latn": "Ukupno PR-ova",
|
||||
no: "Totalt antall PR",
|
||||
be: "Агулам запытаў на змены, PRаў",
|
||||
},
|
||||
"statcard.issues": {
|
||||
en: "Total Issues",
|
||||
@@ -302,6 +307,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Укупно пријављених проблема",
|
||||
"sr-latn": "Ukupno prijavljenih problema",
|
||||
no: "Totalt antall issues",
|
||||
be: "Усяго пытанняў",
|
||||
},
|
||||
"statcard.contribs": {
|
||||
en: "Contributed to (last year)",
|
||||
@@ -350,6 +356,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Доприноси (прошла година)",
|
||||
"sr-latn": "Doprinosi (prošla godina)",
|
||||
no: "Bidro til (i fjor)",
|
||||
be: "Уклад (за мінулы год)",
|
||||
},
|
||||
"statcard.reviews": {
|
||||
en: "Total PRs Reviewed",
|
||||
@@ -398,6 +405,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Укупно прегледаних PR-ова",
|
||||
"sr-latn": "Ukupno pregledanih PR-ova",
|
||||
no: "Totalt antall vurderte PR",
|
||||
be: "Агулам разгледжана PR",
|
||||
},
|
||||
"statcard.discussions-started": {
|
||||
en: "Total Discussions Started",
|
||||
@@ -446,6 +454,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Укупно покренутих дискусија",
|
||||
"sr-latn": "Ukupno pokrenutih diskusija",
|
||||
no: "Totalt antall startede diskusjoner",
|
||||
be: "Агульная колькасць пачатых абмеркаванняў",
|
||||
},
|
||||
"statcard.discussions-answered": {
|
||||
en: "Total Discussions Answered",
|
||||
@@ -494,6 +503,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Укупно одговорених дискусија",
|
||||
"sr-latn": "Ukupno odgovorenih diskusija",
|
||||
no: "Totalt antall besvarte diskusjoner",
|
||||
be: "Усяго адказаў у абмеркаваннях",
|
||||
},
|
||||
"statcard.prs-authored": {
|
||||
en: "PRs Created",
|
||||
@@ -556,6 +566,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Укупно спојених PR-ова",
|
||||
"sr-latn": "Ukupno spojenih PR-ova",
|
||||
no: "Totalt antall sammenslåtte PR",
|
||||
be: "Усяго PR прынята",
|
||||
},
|
||||
"statcard.prs-merged-percentage": {
|
||||
en: "Merged PRs Percentage",
|
||||
@@ -603,6 +614,7 @@ const statCardLocales = ({ name, apostrophe }) => {
|
||||
sr: "Проценат спојених PR-ова",
|
||||
"sr-latn": "Procenat spojenih PR-ova",
|
||||
no: "Prosentandel sammenslåtte PR",
|
||||
be: "Адсотак аб'яднаных PR",
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -655,6 +667,7 @@ const repoCardLocales = {
|
||||
sr: "Шаблон",
|
||||
"sr-latn": "Šablon",
|
||||
no: "Mal",
|
||||
be: "Шаблон",
|
||||
},
|
||||
"repocard.archived": {
|
||||
en: "Archived",
|
||||
@@ -703,6 +716,7 @@ const repoCardLocales = {
|
||||
sr: "Архивирано",
|
||||
"sr-latn": "Arhivirano",
|
||||
no: "Arkivert",
|
||||
be: "Архіваваны",
|
||||
},
|
||||
"repocard.prs-authored": {
|
||||
en: "my created PRs",
|
||||
@@ -769,6 +783,7 @@ const langCardLocales = {
|
||||
sr: "Најкоришћенији језици",
|
||||
"sr-latn": "Najkorišćeniji jezici",
|
||||
no: "Mest brukte språk",
|
||||
be: "Найбольш ужываныя мовы",
|
||||
},
|
||||
"langcard.nodata": {
|
||||
en: "No languages data.",
|
||||
@@ -817,6 +832,7 @@ const langCardLocales = {
|
||||
sr: "Нема података о језицима.",
|
||||
"sr-latn": "Nema podataka o jezicima.",
|
||||
no: "Ingen språkdata.",
|
||||
be: "Няма дадзеных пра мовы.",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -868,6 +884,7 @@ const wakatimeCardLocales = {
|
||||
sr: "WakaTime статистика",
|
||||
"sr-latn": "WakaTime statistika",
|
||||
no: "WakaTime-statistikk",
|
||||
be: "Статыстыка WakaTime",
|
||||
},
|
||||
"wakatimecard.lastyear": {
|
||||
en: "last year",
|
||||
@@ -916,6 +933,7 @@ const wakatimeCardLocales = {
|
||||
sr: "Прошла год.",
|
||||
"sr-latn": "Prošla god.",
|
||||
no: "I fjor",
|
||||
be: "мінулы год",
|
||||
},
|
||||
"wakatimecard.last7days": {
|
||||
en: "last 7 days",
|
||||
@@ -964,6 +982,7 @@ const wakatimeCardLocales = {
|
||||
sr: "Претходних 7 дана",
|
||||
"sr-latn": "Prethodnih 7 dana",
|
||||
no: "Siste 7 dager",
|
||||
be: "апошнія 7 дзён",
|
||||
},
|
||||
"wakatimecard.notpublic": {
|
||||
en: "WakaTime user profile not public",
|
||||
@@ -1012,6 +1031,7 @@ const wakatimeCardLocales = {
|
||||
sr: "WakaTime профил корисника није јаван",
|
||||
"sr-latn": "WakaTime profil korisnika nije javan",
|
||||
no: "WakaTime brukerprofil ikke offentlig",
|
||||
be: "Профіль карыстальніка WakaTime не публічны",
|
||||
},
|
||||
"wakatimecard.nocodedetails": {
|
||||
en: "User doesn't publicly share detailed code statistics",
|
||||
@@ -1062,6 +1082,7 @@ const wakatimeCardLocales = {
|
||||
sr: "Корисник не дели јавно детаљну статистику кода",
|
||||
"sr-latn": "Korisnik ne deli javno detaljnu statistiku koda",
|
||||
no: "Brukeren deler ikke detaljert kodestatistikk offentlig",
|
||||
be: "Карыстальнік не дзеліцца падрабязнай статыстыкай кода публічна",
|
||||
},
|
||||
"wakatimecard.nocodingactivity": {
|
||||
en: "No coding activity this week",
|
||||
@@ -1110,6 +1131,7 @@ const wakatimeCardLocales = {
|
||||
sr: "Током ове недеље није било никаквих активности",
|
||||
"sr-latn": "Tokom ove nedelje nije bilo nikakvih aktivnosti",
|
||||
no: "Ingen kodeaktivitet denne uken",
|
||||
be: "На гэтым тыдні не было актыўнасці",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1126,7 +1148,6 @@ const isLocaleAvailable = (locale) => {
|
||||
};
|
||||
|
||||
export {
|
||||
availableLocales,
|
||||
isLocaleAvailable,
|
||||
langCardLocales,
|
||||
repoCardLocales,
|
||||
@@ -1,4 +1,5 @@
|
||||
import axios from "axios";
|
||||
|
||||
import { storeUser } from "./common/database.js";
|
||||
|
||||
/**
|
||||
@@ -60,7 +61,10 @@ async function githubAuthenticate(code, privateAccess) {
|
||||
const accessToken = body && body.access_token ? body.access_token : null;
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error("OAuth Error: access_token missing from response");
|
||||
throw new Error(
|
||||
"OAuth Error: access_token missing from response: " +
|
||||
JSON.stringify(body),
|
||||
);
|
||||
}
|
||||
|
||||
const userId = await getUserFromToken(accessToken);
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Test Render WakaTime Card should render correctly 1`] = `
|
||||
exports[`Test Render WakaTime Card > should render correctly 1`] = `
|
||||
"
|
||||
<svg
|
||||
width="495"
|
||||
@@ -114,7 +114,7 @@ exports[`Test Render WakaTime Card should render correctly 1`] = `
|
||||
>19 mins</text>
|
||||
|
||||
<svg width="220" x="110" y="4">
|
||||
<rect rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<rect data-testid="progress-background" rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<svg data-testid="lang-progress" width="89.38%">
|
||||
<rect
|
||||
height="8"
|
||||
@@ -137,7 +137,7 @@ exports[`Test Render WakaTime Card should render correctly 1`] = `
|
||||
>1 min</text>
|
||||
|
||||
<svg width="220" x="110" y="4">
|
||||
<rect rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<rect data-testid="progress-background" rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<svg data-testid="lang-progress" width="6.25%">
|
||||
<rect
|
||||
height="8"
|
||||
@@ -158,7 +158,7 @@ exports[`Test Render WakaTime Card should render correctly 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Test Render WakaTime Card should render correctly with compact layout 1`] = `
|
||||
exports[`Test Render WakaTime Card > should render correctly with compact layout 1`] = `
|
||||
"
|
||||
<svg
|
||||
width="495"
|
||||
@@ -310,7 +310,7 @@ exports[`Test Render WakaTime Card should render correctly with compact layout 1
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Test Render WakaTime Card should render correctly with compact layout when langs_count is set 1`] = `
|
||||
exports[`Test Render WakaTime Card > should render correctly with compact layout when langs_count is set 1`] = `
|
||||
"
|
||||
<svg
|
||||
width="495"
|
||||
@@ -462,7 +462,7 @@ exports[`Test Render WakaTime Card should render correctly with compact layout w
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Test Render WakaTime Card should render correctly with percent display format 1`] = `
|
||||
exports[`Test Render WakaTime Card > should render correctly with percent display format 1`] = `
|
||||
"
|
||||
<svg
|
||||
width="495"
|
||||
@@ -576,7 +576,7 @@ exports[`Test Render WakaTime Card should render correctly with percent display
|
||||
>47.39 %</text>
|
||||
|
||||
<svg width="220" x="110" y="4">
|
||||
<rect rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<rect data-testid="progress-background" rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<svg data-testid="lang-progress" width="47.39%">
|
||||
<rect
|
||||
height="8"
|
||||
@@ -599,7 +599,7 @@ exports[`Test Render WakaTime Card should render correctly with percent display
|
||||
>50.48 %</text>
|
||||
|
||||
<svg width="220" x="110" y="4">
|
||||
<rect rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<rect data-testid="progress-background" rx="5" ry="5" x="0" y="0" width="220" height="8" fill="#434d58"></rect>
|
||||
<svg data-testid="lang-progress" width="50.48%">
|
||||
<rect
|
||||
height="8"
|
||||
@@ -0,0 +1,115 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Test /api/wakatime > should render error if user data is not accessible 1`] = `
|
||||
"
|
||||
<svg
|
||||
width="495"
|
||||
height="150"
|
||||
viewBox="0 0 495 150"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
role="img"
|
||||
aria-labelledby="descId"
|
||||
>
|
||||
<title id="titleId"></title>
|
||||
<desc id="descId"></desc>
|
||||
<style>
|
||||
.header {
|
||||
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
|
||||
fill: #2f80ed;
|
||||
animation: fadeInAnimation 0.8s ease-in-out forwards;
|
||||
}
|
||||
@supports(-moz-appearance: auto) {
|
||||
/* Selector detects Firefox */
|
||||
.header { font-size: 15.5px; }
|
||||
}
|
||||
|
||||
|
||||
.stat {
|
||||
font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: #434d58;
|
||||
}
|
||||
@supports(-moz-appearance: auto) {
|
||||
/* Selector detects Firefox */
|
||||
.stat { font-size:12px; }
|
||||
}
|
||||
.stagger {
|
||||
opacity: 0;
|
||||
animation: fadeInAnimation 0.3s ease-in-out forwards;
|
||||
}
|
||||
.not_bold { font-weight: 400 }
|
||||
.bold { font-weight: 700 }
|
||||
|
||||
@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 }
|
||||
#rect-mask rect{
|
||||
animation: slideInAnimation 1s ease-in-out forwards;
|
||||
}
|
||||
.lang-progress{
|
||||
animation: growWidthAnimation 0.6s ease-in-out forwards;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<rect
|
||||
data-testid="card-bg"
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
rx="4.5"
|
||||
height="99%"
|
||||
stroke="#e4e2e2"
|
||||
width="494"
|
||||
fill="#fffefe"
|
||||
stroke-opacity="1"
|
||||
/>
|
||||
|
||||
|
||||
<g
|
||||
data-testid="card-title"
|
||||
transform="translate(25, 35)"
|
||||
>
|
||||
<g transform="translate(0, 0)">
|
||||
<text
|
||||
x="0"
|
||||
y="0"
|
||||
class="header"
|
||||
data-testid="header"
|
||||
>WakaTime Stats</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<g
|
||||
data-testid="main-card-body"
|
||||
transform="translate(0, 55)"
|
||||
>
|
||||
|
||||
<svg x="0" y="0" width="100%">
|
||||
<g transform="translate(0, 0)">
|
||||
<text x="25" y="11" class="stat bold" fill="#434d58">WakaTime user profile not public</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
</g>
|
||||
</svg>
|
||||
"
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { TextDecoder, TextEncoder } from "util";
|
||||
|
||||
//https://stackoverflow.com/a/68468204/1643179
|
||||
Object.assign(global, { TextDecoder, TextEncoder });
|
||||
|
||||
process.env.PAT_1 = "dummyPAT1";
|
||||
process.env.PAT_2 = "dummyPAT2";
|
||||
@@ -0,0 +1,2 @@
|
||||
process.env.GIST_WHITELIST = "bbfce31e0217a3689c8d961a356cb10d";
|
||||
process.env.WHITELIST = "anuraghazra";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user