Skip to content

Commit b248734

Browse files
committed
feat: 添加头部 secret key
1 parent 698c8a1 commit b248734

File tree

9 files changed

+55
-21
lines changed

9 files changed

+55
-21
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SECRET_KEY=

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ Due to network conditions and geographical locations, access to the Gemini API c
2828
```bash
2929
$ curl "http://$YOU_SERVER_HOST:$PORT/api/v1beta/models/gemini-pro:streamGenerateContent?key=$GEMINI_API_TOKEN" \
3030
-H "Content-Type: application/json" \
31+
-H "x-gemini-proxy-secret: $SECRET_KEY" \
3132
-H 'cache-control: no-cache' \
3233
--data-raw '{"contents":[{"role":"user","parts":[{"text":"Hello Gemini"}]}]}' \
3334
--compressed
3435
```
36+
37+
### Parameters
38+
39+
**SECRET_KEY**: User Limitation
40+
41+
- Generate SECRET_KEY using `pnpm secret` and write it to the environment variables.
42+
- If not set, there will be no user limitation.

README.zh-CN.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ Vercel Edge 上的 Gemini API 代理是一项代理服务,旨在解决某些
2828
```bash
2929
$ curl "http://$YOU_SERVER_HOST:$PORT/v1/models/gemini-pro:generateContent?key=$GEMINI_API_TOKEN" \
3030
-H "Content-Type: application/json" \
31+
-H "x-gemini-proxy-secret: $SECRET_KEY" \
3132
-H 'cache-control: no-cache' \
3233
--data-raw '{"contents":[{"role":"user","parts":[{"text":"你好 Gemini"}]}]}' \
3334
--compressed
3435
```
36+
37+
### 参数
38+
39+
**SECRET_KEY**: 用户限制
40+
41+
- 使用 `pnpm secret` 生成 SECRET_KEY,并将其写入环境变量。
42+
- 如果不设置,则不会有用户限制。

__tests__/handleRequest.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('test handleRequest', () => {
1515
],
1616
}
1717

18-
const request = new Request('https://example.com/v1/projects/my-project/locations/us-central1/agents/my-agent/sessions/1234567890:detectIntent?key=123', {
18+
const request = new Request('https://example.com/api/v1/projects/my-project/locations/us-central1/agents/my-agent/sessions/1234567890:detectIntent?key=123', {
1919
method: 'POST',
2020
headers: {
2121
'Content-Type': 'application/json',

__tests__/utils/pickHeaders.spec.ts

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ describe('test utils/pickHeaders', () => {
55
const headers = new Headers()
66
const keys = ['content-type', 'content-length']
77
const picked = pickHeaders(headers, keys)
8-
expect(picked).toEqual(new Headers())
8+
expect(picked).toEqual({})
99
})
1010

1111
it('should return a Headers object with the specified headers', () => {
@@ -15,12 +15,10 @@ describe('test utils/pickHeaders', () => {
1515
headers.set('x-custom-header', 'value')
1616
const keys = ['content-type', 'content-length']
1717
const picked = pickHeaders(headers, keys)
18-
expect(picked).toEqual(
19-
new Headers({
20-
'content-type': 'application/json',
21-
'content-length': '100',
22-
})
23-
)
18+
expect(picked).toEqual({
19+
'content-type': 'application/json',
20+
'content-length': '100',
21+
})
2422
})
2523

2624
it('should return a Headers object with the specified headers using a RegExp key', () => {
@@ -30,12 +28,10 @@ describe('test utils/pickHeaders', () => {
3028
headers.set('x-custom-header', 'value')
3129
const keys = [/^content-/]
3230
const picked = pickHeaders(headers, keys)
33-
expect(picked).toEqual(
34-
new Headers({
35-
'content-type': 'application/json',
36-
'content-length': '100',
37-
})
38-
)
31+
expect(picked).toEqual({
32+
'content-type': 'application/json',
33+
'content-length': '100',
34+
})
3935
})
4036

4137
it('should return a Headers object with the specified headers, including non-string values', () => {
@@ -45,11 +41,9 @@ describe('test utils/pickHeaders', () => {
4541
headers.set('x-custom-header', 'value')
4642
const keys = ['content-type', 'content-length']
4743
const picked = pickHeaders(headers, keys)
48-
expect(picked).toEqual(
49-
new Headers({
50-
'content-type': 'application/json',
51-
'content-length': '100',
52-
})
53-
)
44+
expect(picked).toEqual({
45+
'content-type': 'application/json',
46+
'content-length': '100',
47+
})
5448
})
5549
})

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"ci:coverage": "pnpm test",
99
"postinstall": "node --experimental-modules ./scripts/postinstall.mjs",
1010
"preinstall": "node --experimental-modules ./scripts/preinstall.mjs",
11+
"secret": "node ./scripts/secret.mjs",
1112
"clear": "rimraf --glob ./libs",
1213
"format": "prettier --config .prettierrc.js --write \"**/*.{js,jsx,ts,tsx,d.ts,vue,md,json,yml,yaml}\"",
1314
"lint": "pnpm lint:ts",
@@ -20,12 +21,12 @@
2021
"ok": "pnpm format && pnpm lint && pnpm build && pnpm test"
2122
},
2223
"dependencies": {
24+
"dotenv": "^16.4.5",
2325
"next": "canary",
2426
"react": "canary",
2527
"react-dom": "canary"
2628
},
2729
"devDependencies": {
28-
"json5": "^2.2.3",
2930
"@commitlint/cli": "^19.3.0",
3031
"@commitlint/config-conventional": "^19.2.2",
3132
"@jest/types": "^29.6.3",
@@ -48,6 +49,7 @@
4849
"jest": "^29.7.0",
4950
"jest-junit": "^16.0.0",
5051
"jest-runner-tsd": "^6.0.0",
52+
"json5": "^2.2.3",
5153
"lint-staged": "^15.2.7",
5254
"prettier": "^3.3.3",
5355
"pretty-error": "^4.0.0",

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/secret.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import crypto from 'crypto'
2+
3+
const secretKey = crypto.randomBytes(16).toString('hex')
4+
// eslint-disable-next-line no-console
5+
console.log('Secret Key:', secretKey)

src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import { handleRequest } from './handleRequest'
44
import { createErrorResponse } from './libs/response'
55

66
export default async function proxy(request: Request & { nextUrl?: URL }) {
7+
if (process.env.GEMINI_PROXY_SECRET) {
8+
const secret = request.headers.get('x-gemini-proxy-secret')
9+
if (secret !== process.env.GEMINI_PROXY_SECRET) {
10+
return createErrorResponse('Unauthorized', 401, { statusText: 'Unauthorized' })
11+
}
12+
}
13+
714
const context = createContext(request)
815
const { logger } = context
916

0 commit comments

Comments
 (0)