Skip to content

Commit a186a1f

Browse files
authored
Merge pull request #36 from tranzystorekk/codeberg-endpoints
Add Codeberg endpoints
2 parents eb90417 + 442be75 commit a186a1f

File tree

7 files changed

+164
-0
lines changed

7 files changed

+164
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ NEXT_PUBLIC_WEB_HOST = "127.0.0.1:3000" # Web frontend host
6363
# API Tokens
6464
GITHUB_TOKEN = "ghp_Foo1234567" # Required for GitHub badges
6565
CRATESIO_TOKEN = "cio51fdR1234567" # Required for crates.io badges
66+
CODEBERG_TOKEN = "foobar123456789" # Required for Codeberg badges
6667
```
6768

6869
### spacebadgers
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { NextRequest } from "next/server"
2+
3+
import Badge from '@/utils/Badge'
4+
import Codeberg from '@/utils/Codeberg'
5+
6+
interface Params {
7+
params: {
8+
owner: string
9+
repo: string
10+
}
11+
}
12+
13+
export async function GET(request: NextRequest, { params: { owner, repo } }: Params) {
14+
const closedIssuesCount = await Codeberg.getClient().getIssuesCount({ owner, repo }, { type: 'issues', state: 'closed' })
15+
16+
return await Badge.generate(request, 'closed issues', closedIssuesCount?.toString() ?? 'None')
17+
}
18+
19+
export const runtime = 'edge'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { NextRequest } from "next/server"
2+
3+
import Badge from '@/utils/Badge'
4+
import Codeberg from '@/utils/Codeberg'
5+
6+
interface Params {
7+
params: {
8+
owner: string
9+
repo: string
10+
}
11+
}
12+
13+
export async function GET(request: NextRequest, { params: { owner, repo } }: Params) {
14+
const issuesCount = await Codeberg.getClient().getIssuesCount({ owner, repo }, { type: 'issues', state: 'all' })
15+
16+
return await Badge.generate(request, 'issues', issuesCount?.toString() ?? 'None')
17+
}
18+
19+
export const runtime = 'edge'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { NextRequest } from "next/server"
2+
3+
import Badge from '@/utils/Badge'
4+
import Codeberg from '@/utils/Codeberg'
5+
6+
interface Params {
7+
params: {
8+
owner: string
9+
repo: string
10+
}
11+
}
12+
13+
export async function GET(request: NextRequest, { params: { owner, repo } }: Params) {
14+
const openIssuesCount = await Codeberg.getClient().getIssuesCount({ owner, repo }, { type: 'issues', state: 'open' })
15+
16+
return await Badge.generate(request, 'open issues', openIssuesCount?.toString() ?? 'None')
17+
}
18+
19+
export const runtime = 'edge'
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { NextRequest } from 'next/server'
2+
3+
import Badge from '@/utils/Badge'
4+
import Codeberg from '@/utils/Codeberg'
5+
6+
interface Params {
7+
params: {
8+
owner: string
9+
repo: string
10+
}
11+
}
12+
13+
export async function GET(request: NextRequest, { params: { owner, repo } }: Params) {
14+
const release = await Codeberg.getClient().getLatestRelease({ owner, repo })
15+
const shortestName = [release?.tag_name, release?.name]
16+
.filter(Boolean)
17+
.reduce((a, b) => a!.length < b!.length ? a : b)
18+
19+
return await Badge.generate(request, 'release', shortestName ?? 'None', {
20+
color: !!shortestName ? 'blue' : 'yellow'
21+
})
22+
}
23+
24+
export const runtime = 'edge'

badgers-web/src/app/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ export default function Home() {
160160
<Row name="License" path="/github/license/:owner/:repo" inject={['quintschaf', 'schafkit']} />
161161
</div>
162162
</Section>
163+
<Section name="Codeberg">
164+
<div className="grid grid-cols-[auto_1fr_auto] items-center gap-x-8">
165+
<Row name="Latest release" path="/codeberg/release/:owner/:repo" inject={['forgejo', 'forgejo']} />
166+
<Row name="Issues" path="/codeberg/issues/:owner/:repo" inject={['forgejo', 'forgejo']} />
167+
<Row name="Open issues" path="/codeberg/open-issues/:owner/:repo" inject={['forgejo', 'forgejo']} />
168+
<Row name="Closed issues" path="/codeberg/closed-issues/:owner/:repo" inject={['forgejo', 'forgejo']} />
169+
</div>
170+
</Section>
163171
<Section name="crates.io">
164172
<div className="grid grid-cols-[auto_1fr_auto] items-center gap-x-8">
165173
<Row name="Name" path="/crates/name/:crate" inject={['serde']} />

badgers-web/src/utils/Codeberg.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const API_BASE = 'https://codeberg.org/api/v1';
2+
3+
type ProjectInfo = {
4+
owner: string
5+
repo: string
6+
}
7+
8+
type Repository = {
9+
id: number
10+
default_branch: string
11+
name: string
12+
forks_count: number
13+
stars_count: number
14+
}
15+
16+
type Release = {
17+
name: string
18+
tag_name: string
19+
}
20+
21+
class CodebergClient {
22+
token: string
23+
24+
constructor(token: string) {
25+
this.token = token
26+
}
27+
28+
buildUrl(path: string, query: Record<string, any> = {}): string {
29+
const queryArgs = {
30+
...query,
31+
token: this.token,
32+
}
33+
const queryString = Object
34+
.entries(queryArgs)
35+
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
36+
.join('&')
37+
38+
return `${API_BASE}/${path}?${queryString}`
39+
}
40+
41+
async getRepository({ owner, repo }: ProjectInfo): Promise<Repository | null> {
42+
const repoId = `${owner}/${repo}`
43+
const url = this.buildUrl(`repos/${repoId}`)
44+
const resp = await fetch(url)
45+
46+
if (resp.status !== 200) return null
47+
return await resp.json() as Repository
48+
}
49+
50+
async getIssuesCount({ owner, repo }: ProjectInfo, query: Record<string, any> = {}): Promise<Number | null> {
51+
const repoId = `${owner}/${repo}`
52+
const url = this.buildUrl(`repos/${repoId}/issues`, query)
53+
const resp = await fetch(url)
54+
55+
if (resp.status !== 200) return null
56+
const count = resp.headers.get('x-total-count')
57+
return Number(count)
58+
}
59+
60+
async getLatestRelease({ owner, repo }: ProjectInfo): Promise<Release | null> {
61+
const repoId = `${owner}/${repo}`
62+
const url = this.buildUrl(`repos/${repoId}/releases/latest`)
63+
const resp = await fetch(url)
64+
65+
if (resp.status !== 200) return null
66+
return await resp.json() as Release
67+
}
68+
}
69+
70+
export default class Codeberg {
71+
static getClient(): CodebergClient {
72+
return new CodebergClient(process.env.CODEBERG_TOKEN as string)
73+
}
74+
}

0 commit comments

Comments
 (0)