forked from jsnbuchanan/crowd-funder-for-time-pwa
WIP (fix): improve electron build configuration and service worker handling
- Properly disable service workers in electron builds - Add CSP headers for electron security - Fix path resolution in electron context - Improve preload script error handling and IPC setup - Update build scripts for better electron/capacitor compatibility - Fix router path handling in electron context - Remove electron-builder dependency - Streamline build process and output structure This change improves the stability and security of electron builds while maintaining PWA functionality in web builds. Service workers are now properly disabled in electron context, and path resolution issues are fixed.
This commit is contained in:
@@ -2,99 +2,103 @@ const { app, BrowserWindow } = require("electron");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
// Check if running in dev mode
|
||||
const isDev = process.argv.includes('--inspect');
|
||||
|
||||
function createWindow() {
|
||||
// Add before createWindow function
|
||||
const preloadPath = path.join(__dirname, 'preload.js');
|
||||
console.log('Checking preload path:', preloadPath);
|
||||
console.log('Preload exists:', fs.existsSync(preloadPath));
|
||||
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: true,
|
||||
allowRunningInsecureContent: false,
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
},
|
||||
});
|
||||
|
||||
// Disable service worker in Electron
|
||||
mainWindow.webContents.session.setPermissionRequestHandler(
|
||||
(webContents, permission, callback) => {
|
||||
if (permission === "serviceWorker") {
|
||||
return callback(false);
|
||||
}
|
||||
callback(true);
|
||||
},
|
||||
);
|
||||
|
||||
// Get the correct app path for packaged and development environments
|
||||
const appPath = app.isPackaged ? process.resourcesPath : app.getAppPath();
|
||||
|
||||
// Add debug logging for paths
|
||||
// Debug info
|
||||
console.log("Debug Info:");
|
||||
console.log("Running in dev mode:", isDev);
|
||||
console.log("App is packaged:", app.isPackaged);
|
||||
console.log("Process resource path:", process.resourcesPath);
|
||||
console.log("App path:", appPath);
|
||||
console.log("App path:", app.getAppPath());
|
||||
console.log("__dirname:", __dirname);
|
||||
console.log("process.cwd():", process.cwd());
|
||||
console.log("www path:", path.join(process.resourcesPath, "www"));
|
||||
console.log(
|
||||
"www assets path:",
|
||||
path.join(process.resourcesPath, "www", "assets"),
|
||||
);
|
||||
|
||||
// Try both possible www locations
|
||||
const possiblePaths = [
|
||||
path.join(appPath, "www", "index.html"),
|
||||
path.join(appPath, "..", "www", "index.html"),
|
||||
path.join(process.resourcesPath, "www", "index.html"),
|
||||
];
|
||||
const indexPath = path.join(__dirname, 'www', 'index.html');
|
||||
console.log("www path:", path.join(__dirname, 'www'));
|
||||
console.log("www assets path:", path.join(__dirname, 'www', 'assets'));
|
||||
|
||||
let indexPath;
|
||||
for (const testPath of possiblePaths) {
|
||||
console.log("Testing path:", testPath);
|
||||
if (fs.existsSync(testPath)) {
|
||||
indexPath = testPath;
|
||||
console.log("Found valid path:", indexPath);
|
||||
break;
|
||||
}
|
||||
if (!fs.existsSync(indexPath)) {
|
||||
console.error(`Index file not found at: ${indexPath}`);
|
||||
throw new Error('Index file not found');
|
||||
}
|
||||
|
||||
// Set CSP headers
|
||||
mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
|
||||
callback({
|
||||
responseHeaders: {
|
||||
...details.responseHeaders,
|
||||
'Content-Security-Policy': [
|
||||
"default-src 'self';" +
|
||||
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;" +
|
||||
"font-src 'self' https://fonts.gstatic.com;" +
|
||||
"script-src 'self' 'unsafe-eval' 'unsafe-inline';" +
|
||||
"img-src 'self' data: https:;"
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Load the index.html
|
||||
mainWindow
|
||||
.loadFile(indexPath)
|
||||
.then(() => {
|
||||
console.log("Successfully loaded index.html");
|
||||
// Always open DevTools in packaged app for debugging
|
||||
mainWindow.webContents.openDevTools();
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
console.log("DevTools opened - running in dev mode");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to load index.html:", err);
|
||||
console.error("Attempted path:", indexPath);
|
||||
});
|
||||
|
||||
// Listen for page errors
|
||||
mainWindow.webContents.on(
|
||||
"did-fail-load",
|
||||
(event, errorCode, errorDescription) => {
|
||||
console.error("Page failed to load:", errorCode, errorDescription);
|
||||
},
|
||||
);
|
||||
|
||||
// Listen for console messages from the renderer
|
||||
mainWindow.webContents.on("console-message", (_event, level, message) => {
|
||||
console.log("Renderer Console:", message);
|
||||
});
|
||||
|
||||
// Add right after creating the BrowserWindow
|
||||
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
|
||||
console.error('Page failed to load:', errorCode, errorDescription);
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('preload-error', (event, preloadPath, error) => {
|
||||
console.error('Preload script error:', preloadPath, error);
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
|
||||
console.log('Renderer Console:', message);
|
||||
});
|
||||
|
||||
// Enable remote debugging when in dev mode
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle app ready
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
|
||||
app.on("activate", () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
});
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
// Handle all windows closed
|
||||
app.on("window-all-closed", () => {
|
||||
@@ -103,6 +107,12 @@ app.on("window-all-closed", () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle any errors
|
||||
process.on("uncaughtException", (error) => {
|
||||
console.error("Uncaught Exception:", error);
|
||||
|
||||
@@ -1,5 +1,55 @@
|
||||
const { contextBridge } = require("electron");
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld("api", {
|
||||
logMessage: (message) => console.log(`[Electron]: ${message}`),
|
||||
});
|
||||
// Use a more direct path resolution approach
|
||||
const getPath = (pathType) => {
|
||||
switch(pathType) {
|
||||
case 'userData':
|
||||
return process.env.APPDATA || (
|
||||
process.platform === 'darwin'
|
||||
? `${process.env.HOME}/Library/Application Support`
|
||||
: `${process.env.HOME}/.local/share`
|
||||
);
|
||||
case 'home':
|
||||
return process.env.HOME;
|
||||
case 'appPath':
|
||||
return process.resourcesPath;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
console.log('Preload script starting...');
|
||||
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
// Path utilities
|
||||
getPath,
|
||||
|
||||
// IPC functions
|
||||
send: (channel, data) => {
|
||||
const validChannels = ['toMain'];
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.send(channel, data);
|
||||
}
|
||||
},
|
||||
receive: (channel, func) => {
|
||||
const validChannels = ['fromMain'];
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.on(channel, (event, ...args) => func(...args));
|
||||
}
|
||||
},
|
||||
// Environment info
|
||||
env: {
|
||||
isElectron: true,
|
||||
isDev: process.env.NODE_ENV === 'development'
|
||||
},
|
||||
// Path utilities
|
||||
getBasePath: () => {
|
||||
return process.env.NODE_ENV === 'development' ? '/' : './';
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Preload script completed successfully');
|
||||
} catch (error) {
|
||||
console.error('Error in preload script:', error);
|
||||
}
|
||||
@@ -2,14 +2,11 @@
|
||||
|
||||
import { register } from "register-service-worker";
|
||||
|
||||
// NODE_ENV is "production" by default with "vite build". See https://vitejs.dev/guide/env-and-mode
|
||||
if (import.meta.env.NODE_ENV === "production") {
|
||||
register("/sw_scripts-combined.js", {
|
||||
// Only register service worker if explicitly enabled and in production
|
||||
if (process.env.VITE_PWA_ENABLED === 'true' && process.env.NODE_ENV === "production") {
|
||||
register(`${process.env.BASE_URL}sw.js`, {
|
||||
ready() {
|
||||
console.log(
|
||||
"App is being served from cache by a service worker.\n" +
|
||||
"For more details, visit https://goo.gl/AFskqB",
|
||||
);
|
||||
console.log("Service worker is active.");
|
||||
},
|
||||
registered() {
|
||||
console.log("Service worker has been registered.");
|
||||
@@ -24,12 +21,12 @@ if (import.meta.env.NODE_ENV === "production") {
|
||||
console.log("New content is available; please refresh.");
|
||||
},
|
||||
offline() {
|
||||
console.log(
|
||||
"No internet connection found. App is running in offline mode.",
|
||||
);
|
||||
console.log("No internet connection found. App is running in offline mode.");
|
||||
},
|
||||
error(error) {
|
||||
console.error("Error during service worker registration:", error);
|
||||
},
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("Service worker registration skipped - not enabled or not in production");
|
||||
}
|
||||
|
||||
@@ -284,9 +284,9 @@ const routes: Array<RouteRecordRaw> = [
|
||||
},
|
||||
];
|
||||
|
||||
const isElectron = window.location.protocol === "file:"; // Check if running in Electron
|
||||
const isElectron = window.location.protocol === "file:";
|
||||
const initialPath = isElectron
|
||||
? window.location.pathname.replace("/dist-electron/index.html", "/")
|
||||
? window.location.pathname.split('/dist-electron/www/')[1] || '/'
|
||||
: window.location.pathname;
|
||||
|
||||
const history = isElectron
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import * as path from "path";
|
||||
import { promises as fs } from "fs";
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
export async function loadAppConfig() {
|
||||
const packageJson = await loadPackageJson();
|
||||
const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name;
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
return {
|
||||
pwaConfig: {
|
||||
registerType: "autoUpdate",
|
||||
strategies: "injectManifest",
|
||||
srcDir: ".",
|
||||
filename: "sw_scripts-combined.js",
|
||||
manifest: {
|
||||
name: appName,
|
||||
short_name: appName,
|
||||
@@ -36,34 +34,21 @@ export async function loadAppConfig() {
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
purpose: "maskable",
|
||||
},
|
||||
],
|
||||
share_target: {
|
||||
action: "/share-target",
|
||||
method: "POST",
|
||||
enctype: "multipart/form-data",
|
||||
params: {
|
||||
files: [
|
||||
{
|
||||
name: "photo",
|
||||
accept: ["image/*"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
aliasConfig: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
buffer: path.resolve(__dirname, "node_modules", "buffer"),
|
||||
"dexie-export-import/dist/import":
|
||||
"dexie-export-import/dist/import/index.js",
|
||||
},
|
||||
"@": path.resolve(path.dirname(__dirname), "src"),
|
||||
buffer: path.resolve(path.dirname(__dirname), "node_modules", "buffer"),
|
||||
"dexie-export-import/dist/import": "dexie-export-import/dist/import/index.js",
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function loadPackageJson() {
|
||||
const packageJsonPath = path.resolve(__dirname, "package.json");
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const packageJsonPath = path.resolve(path.dirname(__dirname), "package.json");
|
||||
const packageJsonData = await fs.readFile(packageJsonPath, "utf-8");
|
||||
return JSON.parse(packageJsonData);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user