Skip to content

Commit 7741231

Browse files
fix!: centralize env credentials and remove inline tokens (#77)
1 parent cc193a6 commit 7741231

15 files changed

Lines changed: 150 additions & 135 deletions

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,14 @@ import { fetchSponsors } from '@lizardbyte/contribkit'
225225

226226
const sponsors = await fetchSponsors({
227227
github: {
228-
token,
229228
login,
230229
},
231230
// ...
232231
})
233232
```
234233

234+
Set credentials with environment variables such as `CONTRIBKIT_GITHUB_TOKEN`.
235+
235236
Check the type definition or source code for more utils available.
236237

237238
### Renderers

src/configs/credentials.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import type { ContribkitConfig } from '../types'
2+
import process from 'node:process'
3+
import dotenv from 'dotenv'
4+
5+
export interface EnvCredentials {
6+
github?: {
7+
token?: string
8+
}
9+
patreon?: {
10+
token?: string
11+
}
12+
opencollective?: {
13+
key?: string
14+
}
15+
afdian?: {
16+
token?: string
17+
}
18+
polar?: {
19+
token?: string
20+
}
21+
githubContributors?: {
22+
token?: string
23+
}
24+
gitlabContributors?: {
25+
token?: string
26+
}
27+
githubContributions?: {
28+
token?: string
29+
}
30+
}
31+
32+
interface ConfigWithCredentials extends ContribkitConfig {
33+
credentials?: EnvCredentials
34+
}
35+
36+
export function pruneUndefined<T>(value: T): T {
37+
if (Array.isArray(value))
38+
return value.map(item => pruneUndefined(item)) as T
39+
40+
if (!value || typeof value !== 'object')
41+
return value
42+
43+
return Object.fromEntries(
44+
Object.entries(value)
45+
.filter(([, item]) => item !== undefined)
46+
.map(([key, item]) => [key, pruneUndefined(item)]),
47+
) as T
48+
}
49+
50+
export function loadEnvCredentials(): EnvCredentials {
51+
dotenv.config({ quiet: true })
52+
53+
return pruneUndefined({
54+
github: {
55+
token: process.env.CONTRIBKIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN,
56+
},
57+
patreon: {
58+
token: process.env.CONTRIBKIT_PATREON_TOKEN || process.env.PATREON_TOKEN,
59+
},
60+
opencollective: {
61+
key: process.env.CONTRIBKIT_OPENCOLLECTIVE_KEY || process.env.OPENCOLLECTIVE_KEY,
62+
},
63+
afdian: {
64+
token: process.env.CONTRIBKIT_AFDIAN_TOKEN || process.env.AFDIAN_TOKEN,
65+
},
66+
polar: {
67+
token: process.env.CONTRIBKIT_POLAR_TOKEN || process.env.POLAR_TOKEN,
68+
},
69+
githubContributors: {
70+
token: process.env.CONTRIBKIT_GITHUB_CONTRIBUTORS_TOKEN,
71+
},
72+
gitlabContributors: {
73+
token: process.env.CONTRIBKIT_GITLAB_CONTRIBUTORS_TOKEN,
74+
},
75+
githubContributions: {
76+
token: process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_TOKEN,
77+
},
78+
})
79+
}
80+
81+
export function getCredentials(config: ContribkitConfig): EnvCredentials {
82+
return (config as ConfigWithCredentials).credentials ?? loadEnvCredentials()
83+
}

src/configs/env.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,33 @@
11
import type { ContribkitConfig, GitHubAccountType } from '../types'
22
import process from 'node:process'
33
import dotenv from 'dotenv'
4+
import { loadEnvCredentials, pruneUndefined } from './credentials'
45

5-
function getDeprecatedEnv(name: string, replacement: string) {
6-
const value = process.env[name]
7-
if (value)
8-
console.warn(`[contribkit] env.${name} is deprecated, use env.${replacement} instead`)
9-
return value
6+
export interface EnvConfig {
7+
config: Partial<ContribkitConfig>
8+
credentials: ReturnType<typeof loadEnvCredentials>
109
}
1110

12-
export function loadEnv(): Partial<ContribkitConfig> {
11+
export function loadEnv(): EnvConfig {
1312
dotenv.config({ quiet: true })
1413

1514
const config: Partial<ContribkitConfig> = {
1615
mode: process.env.CONTRIBKIT_MODE as ContribkitConfig['mode'] | undefined,
1716
github: {
1817
login: process.env.CONTRIBKIT_GITHUB_LOGIN || process.env.GITHUB_LOGIN,
19-
token: process.env.CONTRIBKIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN,
2018
type: (process.env.CONTRIBKIT_GITHUB_TYPE || process.env.GITHUB_TYPE) as GitHubAccountType | undefined,
2119
},
22-
patreon: {
23-
token: process.env.CONTRIBKIT_PATREON_TOKEN || process.env.PATREON_TOKEN,
24-
},
2520
opencollective: {
26-
key: process.env.CONTRIBKIT_OPENCOLLECTIVE_KEY || process.env.OPENCOLLECTIVE_KEY,
2721
id: process.env.CONTRIBKIT_OPENCOLLECTIVE_ID || process.env.OPENCOLLECTIVE_ID,
2822
slug: process.env.CONTRIBKIT_OPENCOLLECTIVE_SLUG || process.env.OPENCOLLECTIVE_SLUG,
2923
githubHandle: process.env.CONTRIBKIT_OPENCOLLECTIVE_GH_HANDLE || process.env.OPENCOLLECTIVE_GH_HANDLE,
3024
type: process.env.CONTRIBKIT_OPENCOLLECTIVE_TYPE || process.env.OPENCOLLECTIVE_TYPE,
3125
},
3226
afdian: {
3327
userId: process.env.CONTRIBKIT_AFDIAN_USER_ID || process.env.AFDIAN_USER_ID,
34-
token: process.env.CONTRIBKIT_AFDIAN_TOKEN || process.env.AFDIAN_TOKEN,
3528
exchangeRate: Number.parseFloat(process.env.CONTRIBKIT_AFDIAN_EXCHANGE_RATE || process.env.AFDIAN_EXCHANGE_RATE || '0') || undefined,
3629
},
3730
polar: {
38-
token: process.env.CONTRIBKIT_POLAR_TOKEN || process.env.POLAR_TOKEN,
3931
organization: process.env.CONTRIBKIT_POLAR_ORGANIZATION || process.env.POLAR_ORGANIZATION,
4032
},
4133
liberapay: {
@@ -44,12 +36,10 @@ export function loadEnv(): Partial<ContribkitConfig> {
4436
outputDir: process.env.CONTRIBKIT_DIR,
4537
githubContributors: {
4638
login: process.env.CONTRIBKIT_GITHUB_CONTRIBUTORS_LOGIN,
47-
token: process.env.CONTRIBKIT_GITHUB_CONTRIBUTORS_TOKEN,
4839
minContributions: Number(process.env.CONTRIBKIT_GITHUB_CONTRIBUTORS_MIN) || 1,
4940
repo: process.env.CONTRIBKIT_GITHUB_CONTRIBUTORS_REPO,
5041
},
5142
gitlabContributors: {
52-
token: process.env.CONTRIBKIT_GITLAB_CONTRIBUTORS_TOKEN,
5343
minContributions: Number(process.env.CONTRIBKIT_GITLAB_CONTRIBUTORS_MIN) || 1,
5444
repoId: Number(process.env.CONTRIBKIT_GITLAB_CONTRIBUTORS_REPO_ID),
5545
},
@@ -60,12 +50,14 @@ export function loadEnv(): Partial<ContribkitConfig> {
6050
},
6151
githubContributions: {
6252
login: process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_LOGIN,
63-
token: process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_TOKEN,
6453
maxContributions: Number(process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_MAX) || undefined,
6554
logarithmicScaling: process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_LOGARITHMIC === 'true',
6655
},
6756
}
6857

6958
// remove undefined keys
70-
return JSON.parse(JSON.stringify(config))
59+
return {
60+
config: pruneUndefined(config),
61+
credentials: loadEnvCredentials(),
62+
}
7163
}

src/configs/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export function defineConfig(config: ContribkitConfig): ContribkitConfig {
1414

1515
export async function loadConfig(inlineConfig: ContribkitConfig = {}): Promise<Required<ContribkitConfig>> {
1616
const env = loadEnv()
17+
const envConfig = env.config
1718

1819
const { config = {} } = await _loadConfig<ContribkitConfig>({
1920
sources: [
@@ -39,35 +40,36 @@ export async function loadConfig(inlineConfig: ContribkitConfig = {}): Promise<R
3940
fallbackAvatar: FALLBACK_AVATAR,
4041
includePastSponsors: hasNegativeTier,
4142
...defaultConfig,
42-
...env,
43+
...envConfig,
4344
...config,
4445
...inlineConfig,
4546
github: {
46-
...env.github,
47+
...envConfig.github,
4748
...config.github,
4849
...inlineConfig.github,
4950
},
5051
patreon: {
51-
...env.patreon,
52+
...envConfig.patreon,
5253
...config.patreon,
5354
...inlineConfig.patreon,
5455
},
5556
opencollective: {
56-
...env.opencollective,
57+
...envConfig.opencollective,
5758
...config.opencollective,
5859
...inlineConfig.opencollective,
5960
},
6061
afdian: {
61-
...env.afdian,
62+
...envConfig.afdian,
6263
...config.afdian,
6364
...inlineConfig.afdian,
6465
},
66+
credentials: env.credentials,
6567
} as Required<ContribkitConfig>
6668

6769
if (!['sponsors', 'sponsees'].includes(resolved.mode))
6870
throw new Error(`Invalid mode: ${resolved.mode}. Expected "sponsors" or "sponsees".`)
6971

70-
resolved.name = inlineConfig.name || config.name || env.name || resolved.mode
72+
resolved.name = inlineConfig.name || config.name || envConfig.name || resolved.mode
7173

7274
return resolved
7375
}

src/providers/afdian.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ContribkitConfig, Provider, Sponsorship } from '../types'
22
import { createHash } from 'node:crypto'
33
import { $fetch } from 'ofetch'
4+
import { getCredentials } from '../configs/credentials'
45

56
// afdian api docs https://afdian.net/p/9c65d9cc617011ed81c352540025c377
67

@@ -12,14 +13,13 @@ export const AfdianProvider: Provider = {
1213
return Promise.resolve([])
1314
}
1415

15-
return fetchAfdianSponsors(config.afdian)
16+
return fetchAfdianSponsors(config.afdian, getCredentials(config).afdian?.token)
1617
},
1718
}
1819

19-
export async function fetchAfdianSponsors(options: ContribkitConfig['afdian'] = {}): Promise<Sponsorship[]> {
20+
export async function fetchAfdianSponsors(options: ContribkitConfig['afdian'] = {}, token = getCredentials({}).afdian?.token): Promise<Sponsorship[]> {
2021
const {
2122
userId,
22-
token,
2323
exchangeRate = 6.5,
2424
includePurchases = true,
2525
purchaseEffectivity = 30,

src/providers/crowdinContributors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ export const CrowdinContributorsProvider: Provider = {
1414
name: 'crowdinContributors',
1515
fetchSponsors(config) {
1616
return fetchCrowdinContributors(
17-
config.crowdinContributors?.token || config.token!,
17+
config.crowdinContributors?.token,
1818
config.crowdinContributors?.projectId || 0,
1919
config.crowdinContributors?.minTranslations || 1,
2020
)
2121
},
2222
}
2323

2424
export async function fetchCrowdinContributors(
25-
token: string,
25+
token: string | undefined,
2626
projectId: number,
2727
minTranslations = 1,
2828
): Promise<Sponsorship[]> {

src/providers/github.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ContribkitConfig, GitHubAccountType, Provider, Sponsorship, Tier } from '../types'
22
import { $fetch } from 'ofetch'
3+
import { getCredentials } from '../configs/credentials'
34
import { normalizeUrl } from '../utils'
45

56
function getMonthDifference(startDate: Date, endDate: Date) {
@@ -42,24 +43,24 @@ export const GitHubProvider: Provider = {
4243
fetchSponsors(config) {
4344
if (config.mode === 'sponsees') {
4445
return fetchGitHubSponsoringAsSponsorships(
45-
config.github?.token || config.token!,
46-
config.github?.login || config.login!,
46+
getCredentials(config).github?.token,
47+
config.github?.login,
4748
config.github?.type || 'user',
4849
)
4950
}
5051

5152
return fetchGitHubSponsors(
52-
config.github?.token || config.token!,
53-
config.github?.login || config.login!,
53+
getCredentials(config).github?.token,
54+
config.github?.login,
5455
config.github?.type || 'user',
5556
config,
5657
)
5758
},
5859
}
5960

6061
export async function fetchGitHubSponsors(
61-
token: string,
62-
login: string,
62+
token: string | undefined,
63+
login: string | undefined,
6364
type: GitHubAccountType,
6465
config: ContribkitConfig,
6566
): Promise<Sponsorship[]> {
@@ -360,10 +361,15 @@ export async function fetchGitHubSponsoring(
360361
}
361362

362363
export async function fetchGitHubSponsoringAsSponsorships(
363-
token: string,
364-
login: string,
364+
token: string | undefined,
365+
login: string | undefined,
365366
type: GitHubAccountType,
366367
): Promise<Sponsorship[]> {
368+
if (!token)
369+
throw new Error('GitHub token is required')
370+
if (!login)
371+
throw new Error('GitHub login is required')
372+
367373
// Sponsees mode always loads full history regardless of active status.
368374
const records = await fetchGitHubSponsoring(token, login, type, false)
369375
const recordsBySponsorable = groupSponsoringRecordsByLogin(records)

src/providers/githubContributions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Provider, Sponsorship } from '../types'
22
import { $fetch } from 'ofetch'
3+
import { getCredentials } from '../configs/credentials'
34

45
export const GitHubContributionsProvider: Provider = {
56
name: 'githubContributions',
@@ -8,7 +9,7 @@ export const GitHubContributionsProvider: Provider = {
89
throw new Error('GitHub login is required for githubContributions provider')
910

1011
return fetchGitHubContributions(
11-
config.githubContributions?.token || config.token!,
12+
getCredentials(config).githubContributions?.token,
1213
config.githubContributions.login,
1314
config.githubContributions.maxContributions,
1415
config.githubContributions.logarithmicScaling,
@@ -304,7 +305,7 @@ function convertToSponsorships(
304305
}
305306

306307
export async function fetchGitHubContributions(
307-
token: string,
308+
token: string | undefined,
308309
login: string,
309310
maxContributions?: number,
310311
logarithmicScaling?: boolean,

src/providers/githubContributors.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Provider, Sponsorship } from '../types'
22
import { $fetch } from 'ofetch'
3+
import { getCredentials } from '../configs/credentials'
34

45
export const GitHubContributorsProvider: Provider = {
56
name: 'githubContributors',
@@ -8,17 +9,17 @@ export const GitHubContributorsProvider: Provider = {
89
throw new Error('GitHub repository is required')
910

1011
return fetchGitHubContributors(
11-
config.githubContributors?.token || config.token!,
12-
config.githubContributors?.login || config.login!,
12+
getCredentials(config).githubContributors?.token,
13+
config.githubContributors.login,
1314
config.githubContributors.repo,
1415
config.githubContributors?.minContributions,
1516
)
1617
},
1718
}
1819

1920
export async function fetchGitHubContributors(
20-
token: string,
21-
login: string,
21+
token: string | undefined,
22+
login: string | undefined,
2223
repo: string,
2324
minContributions = 1,
2425
): Promise<Sponsorship[]> {

0 commit comments

Comments
 (0)