update readme and make it extensible

This commit is contained in:
dragsbruh 2026-06-13 14:30:53 +05:30
parent 1745c1cfa7
commit 6cab37bf0a
4 changed files with 84 additions and 2 deletions

54
README.md Normal file
View File

@ -0,0 +1,54 @@
# contribapi
tiny extensible api to merge contribution heatmap from forgejo (git.gay, codeberg, etc)
and github.
## usage
this runs as a cloudflare worker
### configuration
for testing, put these variables in `.dev.vars`. in production you will need to use wrangler
to update worker secrets with this command:
```sh
pnpx wrangler secret put <name>
# it will prompt you for value
```
**1. codeberg**
codeberg (or forgejo in general) do not need api keys to get user heatmap.
```env
CODEBERG_USERNAME=dragsbruh
```
**2. github**
github needs an api key. i recommend you use a fine grained personal access token
with access to commit statuses of all repos.
https://github.com/settings/personal-access-tokens
```env
GITHUB_USERNAME=dragsbruh
GITHUB_TOKEN=github_pat_xxxxxxxxxx
```
**other forgejo instances**
ill use [git.gay](https://git.gay) in this example
- copy `src/forgejo.example.ts` to, `src/gitdotgay.ts`
- read comments in `src/gitdotgay.ts` and change as described, its like 4 changes
- read `queryForges` function in `src/index.ts` and add your query function as shown
to remove github/etc, its the same, you just remove `queryGithub`
### deploying
```sh
pnpm deploy
```

View File

@ -5,7 +5,7 @@ export const codebergResponse = z.array(daySchema);
export async function queryCodeberg(env: Env): Promise<{ codeberg: Day[] }> {
const response = await fetch(
`https://soxy.dragsbruh.workers.dev/?u=https://codeberg.org/api/v1/users/${env.CODEBERG_USERNAME}/heatmap`,
`https://codeberg.org/api/v1/users/${env.CODEBERG_USERNAME}/heatmap`,
{
headers: {
"User-Agent": USER_AGENT,

20
src/forgejo.example.ts Normal file
View File

@ -0,0 +1,20 @@
import { z } from "zod";
import { Day, daySchema, USER_AGENT } from "./common";
// CHANGE `forgejoResponse`
export const forgejoResponse = z.array(daySchema);
// CHANGE `queryForgejo` AND `{ forgejo: Day[] }`
export async function queryForgejo(env: Env): Promise<{ forgejo: Day[] }> {
const response = await fetch(
// CHANGE `https://example.com` and `env.FORGEJO_USERNAME`
`https://example.com/api/v1/users/${env.FORGEJO_USERNAME}/heatmap`,
{
headers: {
"User-Agent": USER_AGENT,
},
},
);
// CHANGE `forgejo:` and `forgejoResponse`
return { forgejo: forgejoResponse.parse(await response.json()) };
}

View File

@ -7,6 +7,14 @@ const corsHeaders = {
"Access-Control-Allow-Headers": "Content-Type",
};
function queryForges(env: Env) {
// CHANGE add/remove forges here
return [
queryGithub(env),
queryCodeberg(env),
];
}
export default {
async fetch(request, env): Promise<Response> {
if (request.method === "OPTIONS") {
@ -17,7 +25,7 @@ export default {
return new Response("OwO wutz that");
}
const responses = await Promise.all([queryGithub(env), queryCodeberg(env)]);
const responses = await Promise.all(queryForges(env));
const merged = Object.assign({}, ...responses);
return Response.json(merged, {