Merge remote-tracking branch 'origin/codex/wuyongjie-bom-1688-realtime-cost' into codex/wuyongjie-bom-estimate-integration-20260817
This commit is contained in:
commit
e674843455
@ -5,6 +5,7 @@ import {
|
||||
canonicalAlibaba1688Resource,
|
||||
extractAlibaba1688AkKeys,
|
||||
getBomCosting1688ImageSearchConfig,
|
||||
resolveBomCostingImageSearchSourceUrl,
|
||||
type BomCosting1688ImageSearchConfig,
|
||||
} from './bom-costing-1688-image-search'
|
||||
|
||||
@ -50,6 +51,13 @@ describe('Alibaba 1688 CSK signing', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('turns only the public same-origin storage route into an absolute image URL', () => {
|
||||
expect(resolveBomCostingImageSearchSourceUrl('/api/storage/object?key=prod%2Fsku.webp')).toEqual(
|
||||
new URL('https://cp.jiansun.vip/api/storage/object?key=prod%2Fsku.webp'),
|
||||
)
|
||||
expect(() => resolveBomCostingImageSearchSourceUrl('/api/auth/me')).toThrow('竞品链接格式不正确')
|
||||
})
|
||||
|
||||
it('sorts canonical query pairs and signs exactly the body sent to the gateway', () => {
|
||||
expect(canonicalAlibaba1688Resource('https://skills-gateway.1688.com/api/find_product/1.0.0?z=2&a=b&a=a')).toBe(
|
||||
'/api/find_product/1.0.0?a=a&a=b&z=2',
|
||||
@ -186,6 +194,36 @@ describe('BomCosting1688ImageSearchService', () => {
|
||||
expect(JSON.parse(request?.body ?? '{}')).not.toHaveProperty('imageUrl')
|
||||
})
|
||||
|
||||
it('fetches a stored same-origin product image through the public application URL', async () => {
|
||||
const imageResponse = {
|
||||
ok: true,
|
||||
status: 200,
|
||||
headers: new Headers({ 'content-type': 'image/jpeg' }),
|
||||
arrayBuffer: async () => Buffer.from('image-bytes'),
|
||||
} as unknown as Response
|
||||
const apiResponse = {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({ success: true, model: { count: 0, data: [] } }),
|
||||
} as unknown as Response
|
||||
const fetch = jest.fn()
|
||||
.mockResolvedValueOnce(imageResponse)
|
||||
.mockResolvedValueOnce(apiResponse)
|
||||
const service = new BomCosting1688ImageSearchService({
|
||||
getConfig: () => config,
|
||||
fetch,
|
||||
now: () => new Date('2026-08-16T10:00:00.000Z'),
|
||||
randomBytes: () => Buffer.from([1, 2, 3, 4]),
|
||||
})
|
||||
|
||||
await expect(service.findRealtimeQuote({
|
||||
imageUrl: '/api/storage/object?key=prod%2Fbom-costing%2Fsku.webp',
|
||||
})).resolves.toMatchObject({ status: 'NO_COMPARABLE_OFFER' })
|
||||
expect(fetch.mock.calls[0]?.[0]).toEqual(
|
||||
new URL('https://cp.jiansun.vip/api/storage/object?key=prod%2Fbom-costing%2Fsku.webp'),
|
||||
)
|
||||
})
|
||||
|
||||
it('degrades without a hidden browser fallback when the service is not configured', async () => {
|
||||
const fetch = jest.fn()
|
||||
const service = new BomCosting1688ImageSearchService({
|
||||
|
||||
@ -10,6 +10,7 @@ import type {
|
||||
|
||||
const OFFICIAL_IMAGE_SEARCH_ENDPOINT = 'https://skills-gateway.1688.com/api/find_product/1.0.0'
|
||||
const IMAGE_SEARCH_PATH = '/api/find_product/1.0.0'
|
||||
const DEFAULT_APPLICATION_ORIGIN = 'https://cp.jiansun.vip'
|
||||
const CSK_SIGNATURE_VERSION = '1.7.0'
|
||||
const DEFAULT_IMAGE_MAX_BYTES = 5 * 1024 * 1024
|
||||
const DEFAULT_TIMEOUT_MS = 45_000
|
||||
@ -167,6 +168,25 @@ export function getBomCosting1688ImageSearchConfig(): BomCosting1688ImageSearchC
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Source snapshots keep object-storage links as same-origin relative paths.
|
||||
* The server-side image-search request needs an absolute URL, but must never
|
||||
* turn arbitrary relative input into an internal-network request. Only the
|
||||
* public storage-object route is eligible for this conversion.
|
||||
*/
|
||||
export function resolveBomCostingImageSearchSourceUrl(rawValue: string): URL {
|
||||
const trimmed = rawValue.trim()
|
||||
if (trimmed.startsWith('/api/storage/object?')) {
|
||||
const configuredOrigin = (
|
||||
process.env.NEXT_PUBLIC_APP_URL ??
|
||||
process.env.NEXTAUTH_URL ??
|
||||
DEFAULT_APPLICATION_ORIGIN
|
||||
).trim()
|
||||
return parseSafeCompetitorUrl(new URL(trimmed, configuredOrigin).toString())
|
||||
}
|
||||
return parseSafeCompetitorUrl(trimmed)
|
||||
}
|
||||
|
||||
const defaultGateway: BomCosting1688ImageSearchGateway = {
|
||||
getConfig: getBomCosting1688ImageSearchConfig,
|
||||
fetch: (...args) => fetch(...args),
|
||||
@ -502,7 +522,7 @@ export class BomCosting1688ImageSearchService {
|
||||
}
|
||||
|
||||
try {
|
||||
const imageUrl = parseSafeCompetitorUrl(sourceImageUrl)
|
||||
const imageUrl = resolveBomCostingImageSearchSourceUrl(sourceImageUrl)
|
||||
const imageResponse = await this.gateway.fetch(imageUrl, {
|
||||
method: 'GET',
|
||||
headers: { Accept: 'image/avif,image/webp,image/png,image/jpeg,image/*;q=0.8' },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user