The phone link no longer depends on the local network. The IDE dials out to turkservis.online and bridges tunnel streams to its own TLS proxy on loopback; the phone runs TLS to the pinned IDE certificate inside the tunnel, so the relay only carries ciphertext. The proxy binds to 127.0.0.1 by default and enabling the feature never asks for a firewall rule; direct same-network access is an explicit option. The window shows the real relay, listener and firewall state, and pairing no longer needs local addresses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8ppceaR5s6cvMnm7uGRnf
51 lines
2.5 KiB
JavaScript
51 lines
2.5 KiB
JavaScript
// "Telefon bağlantısı" window: a thin IPC shell around the controller built in
|
||
// electron/mobile-lan-proxy.ts (createMobileLink). The controller lives in the
|
||
// main process for the whole app run — closing this window only cancels a
|
||
// pairing in progress; paired phones stay connected.
|
||
import { join } from 'node:path'
|
||
import { BrowserWindow, ipcMain } from 'electron'
|
||
|
||
const CODED = /^aiturk-mobile:[a-z_]+$/
|
||
|
||
export function createMobileWindow({ assets, link, show = true }) {
|
||
const window = new BrowserWindow({ width: 560, height: 800, show, title: 'AITURK · Telefon bağlantısı',
|
||
webPreferences: { preload: join(assets, 'mobile-preload.cjs'), contextIsolation: true,
|
||
nodeIntegration: false, sandbox: true } })
|
||
window.setMenu(null)
|
||
window.webContents.setWindowOpenHandler(() => ({ action: 'deny' }))
|
||
window.webContents.on('will-navigate', event => event.preventDefault())
|
||
const actions = {
|
||
state: () => link.state(),
|
||
enable: () => link.enable(),
|
||
disable: () => link.disable(),
|
||
'pair.begin': () => link.pairBegin(),
|
||
'pair.cancel': () => link.pairCancel(),
|
||
devices: () => link.devices(),
|
||
'device.revoke': params => link.deviceRevoke(params?.deviceId),
|
||
// The only actions that may raise a UAC prompt; both are explicit button presses.
|
||
'lan.allow': () => link.lanAllow(),
|
||
'lan.disallow': () => link.lanDisallow(),
|
||
'firewall.allow': () => link.lanAllow(),
|
||
'firewall.retry': () => link.lanAllow(),
|
||
'relay.renew': () => link.relayRenew(),
|
||
'firewall.remove': () => link.firewallRemove(),
|
||
}
|
||
const handler = async (event, action, params) => {
|
||
if (event.sender !== window.webContents || event.senderFrame !== window.webContents.mainFrame) throw new Error('aiturk-mobile:forbidden')
|
||
if (typeof action !== 'string' || !Object.hasOwn(actions, action)) throw new Error('aiturk-mobile:unsupported')
|
||
try { return await actions[action](params) } catch (error) {
|
||
// Only fixed codes cross to the renderer; never upstream error text.
|
||
throw new Error(CODED.test(error?.message) ? error.message : 'aiturk-mobile:failed')
|
||
}
|
||
}
|
||
const unsubscribe = link.onEvent(event => { if (!window.isDestroyed()) window.webContents.send('aiturk-mobile-event', event) })
|
||
ipcMain.handle('aiturk-mobile', handler)
|
||
window.on('closed', () => {
|
||
unsubscribe(); ipcMain.removeHandler('aiturk-mobile')
|
||
// The code on screen is gone with the window; do not leave pairing open behind it.
|
||
void link.pairCancel().catch(() => {})
|
||
})
|
||
void window.loadFile(join(assets, 'mobile-host.html'))
|
||
return window
|
||
}
|