Skip to content

Commit af39dc6

Browse files
committed
chore: in dev builds directly serve from the fs with custom protocol
1 parent f7673a9 commit af39dc6

2 files changed

Lines changed: 56 additions & 8 deletions

File tree

src-electron/config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"stage": "dev",
44
"version": "5.0.5",
55
"productName": "Phoenix Code Experimental Build",
6-
"phoenixLoadURL": "http://localhost:8000/src/",
6+
"phoenixLoadURL": "phtauri://localhost/src/",
77
"trustedElectronDomains": ["phtauri://localhost/", "https://phcode.dev/"],
8-
"gaMetricsURL": "http://localhost:8000/src/desktop-metrics.html"
8+
"gaMetricsURL": "phtauri://localhost/src/desktop-metrics.html"
99
}

src-electron/main.js

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { app, BrowserWindow, protocol, Menu, ipcMain } = require('electron');
1+
const { app, BrowserWindow, protocol, Menu, ipcMain, net } = require('electron');
22
const path = require('path');
33
const fs = require('fs');
44

@@ -10,6 +10,21 @@ const { assertTrusted } = require('./ipc-security');
1010
const { getWindowOptions, trackWindowState, DEFAULTS } = require('./window-state');
1111
const { phoenixLoadURL, gaMetricsURL } = require('./config');
1212

13+
// Register phtauri:// as a privileged scheme (must be done before app ready)
14+
// This enables standard web features: fetch, localStorage, cookies, etc.
15+
protocol.registerSchemesAsPrivileged([
16+
{
17+
scheme: 'phtauri',
18+
privileges: {
19+
standard: true,
20+
secure: true,
21+
supportFetchAPI: true,
22+
corsEnabled: true,
23+
stream: true
24+
}
25+
}
26+
]);
27+
1328
// Request single instance lock - only one instance of the app should run at a time
1429
const gotTheLock = app.requestSingleInstanceLock();
1530

@@ -194,11 +209,45 @@ app.whenReady().then(async () => {
194209
// Remove default menu bar
195210
Menu.setApplicationMenu(null);
196211

212+
// Register phtauri:// protocol for serving Phoenix files
213+
// In dev: serves from ../phoenix/ repo
214+
// In packaged: would need different handling (but typically uses https:// in production)
215+
if (!app.isPackaged) {
216+
const phoenixRepoPath = path.resolve(__dirname, '..', '..', 'phoenix');
217+
218+
protocol.handle('phtauri', (request) => {
219+
try {
220+
const url = new URL(request.url);
221+
// phtauri://localhost/src/index.html -> ../phoenix/src/index.html
222+
let requestedPath = decodeURIComponent(url.pathname);
223+
224+
// Serve index.html for directory requests
225+
if (requestedPath.endsWith('/')) {
226+
requestedPath += 'index.html';
227+
}
228+
229+
const filePath = path.join(phoenixRepoPath, requestedPath);
230+
const normalizedFilePath = path.normalize(filePath);
231+
232+
// Security: Ensure path is under phoenix repo (prevent directory traversal)
233+
if (!normalizedFilePath.startsWith(phoenixRepoPath)) {
234+
console.error('phtauri access denied - path not under phoenix repo:', requestedPath);
235+
return new Response('Access denied', { status: 403 });
236+
}
237+
238+
return net.fetch(`file://${normalizedFilePath}`);
239+
} catch (err) {
240+
console.error('phtauri protocol error:', err);
241+
return new Response('Not found', { status: 404 });
242+
}
243+
});
244+
}
245+
197246
// Register asset:// protocol for serving local files from appLocalData/assets/
198247
const appDataDir = getAppDataDir();
199248
const assetsDir = path.join(appDataDir, 'assets');
200249

201-
protocol.registerFileProtocol('asset', (request, callback) => {
250+
protocol.handle('asset', (request) => {
202251
try {
203252
const url = new URL(request.url);
204253
// Decode the path from URL encoding
@@ -209,14 +258,13 @@ app.whenReady().then(async () => {
209258
// Security: Ensure path is under assets directory (prevent directory traversal)
210259
if (!normalizedRequested.startsWith(normalizedAssetsDir)) {
211260
console.error('Asset access denied - path not under assets dir:', requestedPath);
212-
callback({ error: -10 }); // net::ERR_ACCESS_DENIED
213-
return;
261+
return new Response('Access denied', { status: 403 });
214262
}
215263

216-
callback({ path: normalizedRequested });
264+
return net.fetch(`file://${normalizedRequested}`);
217265
} catch (err) {
218266
console.error('Asset protocol error:', err);
219-
callback({ error: -2 }); // net::ERR_FAILED
267+
return new Response('Not found', { status: 404 });
220268
}
221269
});
222270

0 commit comments

Comments
 (0)