리뷰 지적 반영. 기존엔 HTTP 공인 egress IP 만 100.64/10 검사라, 전형적 CGNAT (egress 는 ISP 공인, 라우터 WAN 만 100.64)을 놓치고 "CGNAT 아님"처럼 오진 가능. - 라우터 WAN(IGD 외부) IP 를 UPnP 로 조회해 HTTP 공인 IP 와 비교. - cgnat 를 3-state(yes/no/unknown)로: WAN 이 사설/CGNAT 이거나 공인 IP 와 다르면 yes, 같으면 no, WAN 못 읽으면 unknown. - unknown 이면 "외부 IP만으론 CGNAT 확정/배제 불가, 라우터 WAN 확인" 안내. - WAN 조회 타임아웃 8s→6s. v0.3.17. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
42 lines
1.2 KiB
TypeScript
42 lines
1.2 KiB
TypeScript
import { contextBridge, ipcRenderer } from 'electron'
|
|
|
|
interface PortForwardOutcome {
|
|
externalIp: string
|
|
localIp: string
|
|
wanIp: string
|
|
cgnat: 'yes' | 'no' | 'unknown'
|
|
port: number
|
|
reachable: boolean | null
|
|
preForwarded: boolean
|
|
detail: string
|
|
}
|
|
|
|
const api = {
|
|
/** i18n 사전을 렌더러에 전달. */
|
|
loadLocale: (): Promise<Record<string, unknown>> => ipcRenderer.invoke('pf:i18n:dict'),
|
|
|
|
/** 지정 포트를 UPnP 로 개방 시도하고 외부에서 닿는지 점검. */
|
|
openPort: (port: number): Promise<PortForwardOutcome> => ipcRenderer.invoke('pf:open', port),
|
|
|
|
/** UPnP 포트 매핑 제거. */
|
|
closePort: (port: number): Promise<void> => ipcRenderer.invoke('pf:close', port),
|
|
|
|
/** 프로그램 종료. */
|
|
quit: (): Promise<void> => ipcRenderer.invoke('pf:quit'),
|
|
|
|
/** 로그 스트림 구독. */
|
|
onLog: (handler: (line: string) => void): (() => void) => {
|
|
const listener = (_event: unknown, line: string) => handler(line)
|
|
ipcRenderer.on('log', listener)
|
|
return () => ipcRenderer.removeListener('log', listener)
|
|
}
|
|
}
|
|
|
|
contextBridge.exposeInMainWorld('pfTool', api)
|
|
|
|
declare global {
|
|
interface Window {
|
|
pfTool: typeof api
|
|
}
|
|
}
|