Merge H5 active port fix into local main

Bring the detached worktree fix back to local main so H5 QR and copied LAN links no longer keep stale sidecar ports after app restarts.

Constraint: Local main already had newer unrelated work after the detached worktree base.
Confidence: high
Scope-risk: narrow
Directive: Keep H5 LAN URL host preservation separate from reverse proxy URL preservation.
Tested: bun test src/server/__tests__/h5-access-service.test.ts
Tested: bun test src/server/__tests__/h5-access-api.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/h5-access-policy.test.ts
Tested: bun run check:server
Not-tested: Full desktop packaged build after merge.
This commit is contained in:
程序员阿江(Relakkes) 2026-05-23 17:09:30 +08:00
commit d1ed077f3e
2 changed files with 44 additions and 2 deletions

View File

@ -101,7 +101,7 @@ describe('H5AccessService', () => {
expect(result.settings.publicBaseUrl).toBe('https://chat.example.com/app')
})
test('auto LAN mode fills blank or loopback URLs but preserves manual LAN URLs', () => {
test('auto LAN mode fills blank or loopback URLs and refreshes manual LAN ports', () => {
expect(resolveEffectiveH5PublicBaseUrl({
enabled: true,
storedPublicBaseUrl: null,
@ -121,7 +121,7 @@ describe('H5AccessService', () => {
storedPublicBaseUrl: 'http://192.168.1.100:54064',
configuredPublicBaseUrl: null,
autoPublicBaseUrl: 'http://172.20.16.1:39876',
})).toBe('http://192.168.1.100:54064')
})).toBe('http://192.168.1.100:39876')
expect(resolveEffectiveH5PublicBaseUrl({
enabled: true,
@ -131,6 +131,22 @@ describe('H5AccessService', () => {
})).toBe('https://chat.example.com/app')
})
test('auto LAN mode keeps full reverse proxy URLs intact', () => {
expect(resolveEffectiveH5PublicBaseUrl({
enabled: true,
storedPublicBaseUrl: 'https://192.168.1.100:8443',
configuredPublicBaseUrl: null,
autoPublicBaseUrl: 'http://192.168.1.100:39876',
})).toBe('https://192.168.1.100:8443')
expect(resolveEffectiveH5PublicBaseUrl({
enabled: true,
storedPublicBaseUrl: 'http://192.168.1.100:8080/h5',
configuredPublicBaseUrl: null,
autoPublicBaseUrl: 'http://192.168.1.100:39876',
})).toBe('http://192.168.1.100:8080/h5')
})
test('auto LAN detection prefers physical adapters over WSL and Docker virtual adapters', () => {
expect(findPrivateLanAddress({
'vEthernet (WSL)': [{

View File

@ -179,6 +179,11 @@ export function resolveEffectiveH5PublicBaseUrl({
return autoPublicBaseUrl
}
const refreshedLanUrl = refreshLanPublicBaseUrlPort(storedPublicBaseUrl, autoPublicBaseUrl)
if (refreshedLanUrl) {
return refreshedLanUrl
}
return storedPublicBaseUrl
}
@ -203,6 +208,27 @@ function isLocalHost(hostname: string): boolean {
hostname === '::'
}
function refreshLanPublicBaseUrlPort(storedPublicBaseUrl: string, autoPublicBaseUrl: string): string | null {
try {
const stored = new URL(storedPublicBaseUrl)
const auto = new URL(autoPublicBaseUrl)
const storedPath = stored.pathname.replace(/\/+$/, '')
if (
stored.protocol !== 'http:' ||
storedPath !== '' ||
!isPrivateIPv4(stored.hostname) ||
!auto.port
) {
return null
}
return `${stored.protocol}//${stored.hostname}:${auto.port}`
} catch {
return null
}
}
type NetworkInterfaces = ReturnType<typeof os.networkInterfaces>
const PHYSICAL_INTERFACE_RE = /\b(wi-?fi|wlan|wireless|ethernet|lan|en\d+|eth\d+)\b/i