Skip to content

Commit 79b7586

Browse files
WuMingDaoclaude
andcommitted
refactor(api): extract URL utils and apply HF image proxy
- Extract getOrigin and toProxyUrl from openai/routes.ts to utils/url.ts - Apply HuggingFace image proxy in /api/generate and /api/generate-hf - Add unit tests for toProxyUrl - Update biome schema to 2.3.11 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 48dcae7 commit 79b7586

6 files changed

Lines changed: 73 additions & 32 deletions

File tree

apps/api/src/app.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import {
4848
import { registerOpenAIRoutes } from './openai/routes'
4949
import { getProvider, hasProvider } from './providers'
5050
import { createVideoTask, getVideoTaskStatus } from './providers/gitee'
51-
import { callGradioApi, formatDimensions, formatDuration } from './utils'
51+
import { callGradioApi, formatDimensions, formatDuration, getOrigin, toProxyUrl } from './utils'
5252

5353
export interface AppConfig {
5454
corsOrigins?: string[]
@@ -507,7 +507,7 @@ function createApiApp(config: AppConfig = {}) {
507507
const providerName = providerConfig?.name || providerId
508508

509509
const imageDetails: ImageDetails = {
510-
url: result.url,
510+
url: providerId === 'huggingface' ? toProxyUrl(getOrigin(c), result.url) : result.url,
511511
provider: providerName,
512512
model: modelName,
513513
dimensions: formatDimensions(width, height),
@@ -580,7 +580,7 @@ function createApiApp(config: AppConfig = {}) {
580580
const modelName = modelConfig?.name || modelId
581581

582582
const imageDetails: ImageDetails = {
583-
url: result.url,
583+
url: toProxyUrl(getOrigin(c), result.url),
584584
provider: 'HuggingFace',
585585
model: modelName,
586586
dimensions: formatDimensions(width, height),

apps/api/src/openai/routes.ts

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,12 @@
11
import { Errors, getModelsByProvider, PROVIDER_CONFIGS } from '@z-image/shared'
2-
import type { Context, Hono } from 'hono'
2+
import type { Hono } from 'hono'
33
import { sendError } from '../middleware'
44
import { getProvider } from '../providers'
5+
import { getOrigin, toProxyUrl } from '../utils'
56
import { convertRequest, convertResponse, parseBearerToken } from './adapter'
67
import { resolveModel } from './model-resolver'
78
import type { OpenAIImageRequest, OpenAIModelsListResponse } from './types'
89

9-
function getOrigin(c: Context): string {
10-
try {
11-
return new URL(c.req.url).origin
12-
} catch {
13-
return ''
14-
}
15-
}
16-
17-
function toProxyUrl(origin: string, url: string): string {
18-
if (!origin) return url
19-
20-
try {
21-
const parsed = new URL(url)
22-
if (parsed.hostname.endsWith('.hf.space') && parsed.pathname.startsWith('/gradio_api/file=')) {
23-
return `${origin}/api/proxy-image?url=${encodeURIComponent(url)}`
24-
}
25-
} catch {
26-
// ignore invalid URL, fall back to raw string checks
27-
}
28-
29-
if (url.includes('.hf.space/gradio_api/file=')) {
30-
return `${origin}/api/proxy-image?url=${encodeURIComponent(url)}`
31-
}
32-
33-
return url
34-
}
35-
3610
function listModels(): OpenAIModelsListResponse {
3711
const created = 1700000000
3812
const giteeModels = getModelsByProvider('gitee').map((m) => m.id)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Utils Unit Tests
3+
* Tests for URL utilities
4+
*/
5+
6+
import { describe, expect, it } from 'vitest'
7+
import { toProxyUrl } from '../url'
8+
9+
describe('toProxyUrl', () => {
10+
it('should return original url when origin is empty', () => {
11+
expect(toProxyUrl('', 'https://example.com/image.png')).toBe('https://example.com/image.png')
12+
})
13+
14+
it('should proxy HuggingFace gradio file URLs', () => {
15+
const origin = 'https://example.com'
16+
const url = 'https://abc123.hf.space/gradio_api/file=image.png'
17+
expect(toProxyUrl(origin, url)).toBe(
18+
'https://example.com/api/proxy-image?url=https%3A%2F%2Fabc123.hf.space%2Fgradio_api%2Ffile%3Dimage.png'
19+
)
20+
})
21+
22+
it('should proxy via string fallback for non-URL inputs', () => {
23+
const origin = 'https://example.com'
24+
const url = 'abc123.hf.space/gradio_api/file=image.png'
25+
expect(toProxyUrl(origin, url)).toBe(
26+
'https://example.com/api/proxy-image?url=abc123.hf.space%2Fgradio_api%2Ffile%3Dimage.png'
27+
)
28+
})
29+
30+
it('should not proxy unrelated URLs', () => {
31+
expect(toProxyUrl('https://example.com', 'https://abc123.hf.space/file=image.png')).toBe(
32+
'https://abc123.hf.space/file=image.png'
33+
)
34+
expect(toProxyUrl('https://example.com', 'https://example.com/image.png')).toBe(
35+
'https://example.com/image.png'
36+
)
37+
})
38+
})

apps/api/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
export * from './format'
66
export * from './gradio'
7+
export * from './url'

apps/api/src/utils/url.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { Context } from 'hono'
2+
3+
export function getOrigin(c: Context): string {
4+
try {
5+
return new URL(c.req.url).origin
6+
} catch {
7+
return ''
8+
}
9+
}
10+
11+
export function toProxyUrl(origin: string, url: string): string {
12+
if (!origin) return url
13+
14+
try {
15+
const parsed = new URL(url)
16+
if (parsed.hostname.endsWith('.hf.space') && parsed.pathname.startsWith('/gradio_api/file=')) {
17+
return `${origin}/api/proxy-image?url=${encodeURIComponent(url)}`
18+
}
19+
} catch {
20+
// ignore invalid URL, fall back to raw string checks
21+
}
22+
23+
if (url.includes('.hf.space/gradio_api/file=')) {
24+
return `${origin}/api/proxy-image?url=${encodeURIComponent(url)}`
25+
}
26+
27+
return url
28+
}

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
33
"vcs": {
44
"enabled": true,
55
"clientKind": "git",

0 commit comments

Comments
 (0)