mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
Load packaged renderer over localhost
This commit is contained in:
@@ -46,6 +46,7 @@ import {
|
||||
isHudOverlayMousePassthroughSupported,
|
||||
showUpdateToastWindow,
|
||||
} from "./windows";
|
||||
import { ensurePackagedRendererServer } from "./rendererServer";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const IS_SMOKE_EXPORT = process.env.RECORDLY_SMOKE_EXPORT === "1";
|
||||
@@ -747,6 +748,14 @@ app.whenReady().then(async () => {
|
||||
// Ensure recordings directory exists
|
||||
await ensureRecordingsDir();
|
||||
|
||||
if (!VITE_DEV_SERVER_URL) {
|
||||
try {
|
||||
await ensurePackagedRendererServer(RENDERER_DIST);
|
||||
} catch (error) {
|
||||
console.warn("[renderer-server] Failed to start packaged renderer server:", error);
|
||||
}
|
||||
}
|
||||
|
||||
registerIpcHandlers(
|
||||
createEditorWindowWrapper,
|
||||
createSourceSelectorWindowWrapper,
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const MIME_TYPES: Record<string, string> = {
|
||||
".css": "text/css; charset=utf-8",
|
||||
".gif": "image/gif",
|
||||
".html": "text/html; charset=utf-8",
|
||||
".ico": "image/x-icon",
|
||||
".js": "text/javascript; charset=utf-8",
|
||||
".json": "application/json; charset=utf-8",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".mjs": "text/javascript; charset=utf-8",
|
||||
".png": "image/png",
|
||||
".svg": "image/svg+xml",
|
||||
".txt": "text/plain; charset=utf-8",
|
||||
".wasm": "application/wasm",
|
||||
".webp": "image/webp",
|
||||
".woff": "font/woff",
|
||||
".woff2": "font/woff2",
|
||||
};
|
||||
|
||||
let packagedRendererBaseUrl: string | null = null;
|
||||
let packagedRendererServerStartPromise: Promise<string> | null = null;
|
||||
|
||||
function getContentType(filePath: string): string {
|
||||
return MIME_TYPES[path.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
||||
}
|
||||
|
||||
function resolveRequestedFilePath(rootDir: string, requestPathname: string): string | null {
|
||||
const trimmedPathname = requestPathname === "/" ? "/index.html" : requestPathname;
|
||||
const relativePath = path.normalize(decodeURIComponent(trimmedPathname)).replace(/^\/+/, "");
|
||||
const resolvedRootDir = path.resolve(rootDir);
|
||||
const resolvedFilePath = path.resolve(resolvedRootDir, relativePath);
|
||||
|
||||
if (
|
||||
resolvedFilePath !== resolvedRootDir &&
|
||||
!resolvedFilePath.startsWith(`${resolvedRootDir}${path.sep}`)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return resolvedFilePath;
|
||||
}
|
||||
|
||||
async function servePackagedRendererRequest(
|
||||
rootDir: string,
|
||||
request: IncomingMessage,
|
||||
response: ServerResponse,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const requestUrl = new URL(request.url ?? "/", "http://127.0.0.1");
|
||||
const resolvedFilePath = resolveRequestedFilePath(rootDir, requestUrl.pathname);
|
||||
|
||||
if (!resolvedFilePath) {
|
||||
response.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
|
||||
response.end("Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = resolvedFilePath;
|
||||
const fileContents = await fs.readFile(filePath);
|
||||
|
||||
response.writeHead(200, {
|
||||
"Cache-Control": "no-cache",
|
||||
"Content-Type": getContentType(filePath),
|
||||
});
|
||||
|
||||
if (request.method === "HEAD") {
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
response.end(fileContents);
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException)?.code === "ENOENT") {
|
||||
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
||||
response.end("Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
response.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
||||
response.end("Internal Server Error");
|
||||
}
|
||||
}
|
||||
|
||||
export function getPackagedRendererBaseUrl(): string | null {
|
||||
return packagedRendererBaseUrl;
|
||||
}
|
||||
|
||||
export async function ensurePackagedRendererServer(rootDir: string): Promise<string> {
|
||||
if (packagedRendererBaseUrl) {
|
||||
return packagedRendererBaseUrl;
|
||||
}
|
||||
|
||||
if (packagedRendererServerStartPromise) {
|
||||
return packagedRendererServerStartPromise;
|
||||
}
|
||||
|
||||
packagedRendererServerStartPromise = new Promise((resolve, reject) => {
|
||||
const server = createServer((request, response) => {
|
||||
void servePackagedRendererRequest(rootDir, request, response);
|
||||
});
|
||||
|
||||
server.once("error", (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
server.listen(0, "127.0.0.1", () => {
|
||||
const address = server.address();
|
||||
if (!address || typeof address === "string") {
|
||||
server.close();
|
||||
reject(new Error("Renderer server did not expose a TCP address"));
|
||||
return;
|
||||
}
|
||||
|
||||
packagedRendererBaseUrl = `http://127.0.0.1:${address.port}`;
|
||||
resolve(packagedRendererBaseUrl);
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
return await packagedRendererServerStartPromise;
|
||||
} finally {
|
||||
packagedRendererServerStartPromise = null;
|
||||
}
|
||||
}
|
||||
+14
-4
@@ -5,6 +5,7 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { app, BrowserWindow, ipcMain } from "electron";
|
||||
import { USER_DATA_PATH } from "./appPaths";
|
||||
import { getPackagedRendererBaseUrl } from "./rendererServer";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const nodeRequire = createRequire(import.meta.url);
|
||||
@@ -643,10 +644,19 @@ export function createEditorWindow(): BrowserWindow {
|
||||
const query = new URLSearchParams(getEditorWindowQuery());
|
||||
win.loadURL(`${VITE_DEV_SERVER_URL}?${query.toString()}`);
|
||||
} else {
|
||||
console.log("[editor-window] load-file", path.join(RENDERER_DIST, "index.html"));
|
||||
win.loadFile(path.join(RENDERER_DIST, "index.html"), {
|
||||
query: getEditorWindowQuery(),
|
||||
});
|
||||
const query = new URLSearchParams(getEditorWindowQuery());
|
||||
const packagedRendererBaseUrl = getPackagedRendererBaseUrl();
|
||||
|
||||
if (packagedRendererBaseUrl) {
|
||||
const targetUrl = `${packagedRendererBaseUrl}/?${query.toString()}`;
|
||||
console.log("[editor-window] load-url", targetUrl);
|
||||
win.loadURL(targetUrl);
|
||||
} else {
|
||||
console.log("[editor-window] load-file", path.join(RENDERER_DIST, "index.html"));
|
||||
win.loadFile(path.join(RENDERER_DIST, "index.html"), {
|
||||
query: getEditorWindowQuery(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return win;
|
||||
|
||||
Reference in New Issue
Block a user