You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
7.7 KiB
236 lines
7.7 KiB
const { app, BrowserWindow, session, protocol, dialog } = require("electron");
|
|
const path = require("path");
|
|
const fs = require("fs");
|
|
|
|
// Global window reference
|
|
let mainWindow = null;
|
|
|
|
// Debug flags
|
|
const isDev = !app.isPackaged;
|
|
|
|
// Helper for logging
|
|
function logDebug(...args) {
|
|
// eslint-disable-next-line no-console
|
|
console.log("[DEBUG]", ...args);
|
|
}
|
|
|
|
function logError(...args) {
|
|
// eslint-disable-next-line no-console
|
|
console.error("[ERROR]", ...args);
|
|
if (!isDev && mainWindow) {
|
|
dialog.showErrorBox("TimeSafari Error", args.join(" "));
|
|
}
|
|
}
|
|
|
|
// Get the most appropriate app path
|
|
function getAppPath() {
|
|
if (app.isPackaged) {
|
|
const possiblePaths = [
|
|
path.join(process.resourcesPath, "app.asar", "dist-electron"),
|
|
path.join(process.resourcesPath, "app.asar"),
|
|
path.join(process.resourcesPath, "app"),
|
|
app.getAppPath(),
|
|
];
|
|
|
|
for (const testPath of possiblePaths) {
|
|
const testFile = path.join(testPath, "www", "index.html");
|
|
if (fs.existsSync(testFile)) {
|
|
logDebug(`Found valid app path: ${testPath}`);
|
|
return testPath;
|
|
}
|
|
}
|
|
|
|
logError("Could not find valid app path");
|
|
return path.join(process.resourcesPath, "app.asar"); // Default fallback
|
|
} else {
|
|
return __dirname;
|
|
}
|
|
}
|
|
|
|
// Create the browser window
|
|
function createWindow() {
|
|
logDebug("Creating window with paths:");
|
|
logDebug("- process.resourcesPath:", process.resourcesPath);
|
|
logDebug("- app.getAppPath():", app.getAppPath());
|
|
logDebug("- __dirname:", __dirname);
|
|
|
|
// Create the browser window
|
|
mainWindow = new BrowserWindow({
|
|
width: 1200,
|
|
height: 800,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, "preload.js"),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
webSecurity: true,
|
|
},
|
|
});
|
|
|
|
// Fix root file paths - replaces all protocol handling
|
|
protocol.interceptFileProtocol("file", (request, callback) => {
|
|
let urlPath = request.url.substr(7); // Remove 'file://' prefix
|
|
urlPath = decodeURIComponent(urlPath); // Handle special characters
|
|
|
|
// Debug all asset requests
|
|
if (
|
|
urlPath.includes("assets/") ||
|
|
urlPath.endsWith(".js") ||
|
|
urlPath.endsWith(".css") ||
|
|
urlPath.endsWith(".html")
|
|
) {
|
|
logDebug(`Intercepted request for: ${urlPath}`);
|
|
}
|
|
|
|
// Fix paths for files at root like registerSW.js or manifest.webmanifest
|
|
if (
|
|
urlPath.endsWith("registerSW.js") ||
|
|
urlPath.endsWith("manifest.webmanifest") ||
|
|
urlPath.endsWith("sw.js")
|
|
) {
|
|
const appBasePath = getAppPath();
|
|
const filePath = path.join(appBasePath, "www", path.basename(urlPath));
|
|
|
|
if (fs.existsSync(filePath)) {
|
|
logDebug(`Serving ${urlPath} from ${filePath}`);
|
|
return callback({ path: filePath });
|
|
} else {
|
|
// For service worker, provide empty content to avoid errors
|
|
if (urlPath.endsWith("registerSW.js") || urlPath.endsWith("sw.js")) {
|
|
logDebug(`Providing empty SW file for ${urlPath}`);
|
|
// Create an empty JS file content that does nothing
|
|
const tempFile = path.join(
|
|
app.getPath("temp"),
|
|
path.basename(urlPath),
|
|
);
|
|
fs.writeFileSync(
|
|
tempFile,
|
|
"// Service workers disabled in Electron\n",
|
|
);
|
|
return callback({ path: tempFile });
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle assets paths that might be requested from root
|
|
if (urlPath.startsWith("/assets/") || urlPath === "/assets") {
|
|
const appBasePath = getAppPath();
|
|
const filePath = path.join(appBasePath, "www", urlPath);
|
|
logDebug(`Redirecting ${urlPath} to ${filePath}`);
|
|
return callback({ path: filePath });
|
|
}
|
|
|
|
// Handle assets paths that are missing the www folder
|
|
if (urlPath.includes("/assets/")) {
|
|
const appBasePath = getAppPath();
|
|
const relativePath = urlPath.substring(urlPath.indexOf("/assets/"));
|
|
const filePath = path.join(appBasePath, "www", relativePath);
|
|
if (fs.existsSync(filePath)) {
|
|
logDebug(`Fixing asset path ${urlPath} to ${filePath}`);
|
|
return callback({ path: filePath });
|
|
}
|
|
}
|
|
|
|
// For all other paths, just pass them through
|
|
callback({ path: urlPath });
|
|
});
|
|
|
|
// Set up CSP headers - more permissive in dev mode
|
|
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
|
callback({
|
|
responseHeaders: {
|
|
...details.responseHeaders,
|
|
"Content-Security-Policy": [
|
|
isDev
|
|
? "default-src 'self' file:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://*; connect-src 'self' https://*"
|
|
: "default-src 'self' file:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://image.timesafari.app https://*.americancloud.com; connect-src 'self' https://api.timesafari.app https://api.endorser.ch https://test-api.endorser.ch https://fonts.googleapis.com",
|
|
],
|
|
},
|
|
});
|
|
});
|
|
|
|
// Load the index.html with modifications
|
|
try {
|
|
const appPath = getAppPath();
|
|
const wwwFolder = path.join(appPath, "www");
|
|
const indexPath = path.join(wwwFolder, "index.html");
|
|
|
|
logDebug("Loading app from:", indexPath);
|
|
|
|
// Check if the file exists
|
|
if (fs.existsSync(indexPath)) {
|
|
// Read and modify index.html to disable service worker
|
|
let indexContent = fs.readFileSync(indexPath, "utf8");
|
|
|
|
// 1. Add base tag for proper path resolution
|
|
indexContent = indexContent.replace(
|
|
"<head>",
|
|
`<head>\n <base href="file://${wwwFolder}/">`,
|
|
);
|
|
|
|
// 2. Disable service worker registration by replacing the script
|
|
if (indexContent.includes("registerSW.js")) {
|
|
indexContent = indexContent.replace(
|
|
/<script src="registerSW\.js"><\/script>/,
|
|
"<script>/* Service worker disabled in Electron */</script>",
|
|
);
|
|
}
|
|
|
|
// Create a temp file with modified content
|
|
const tempDir = app.getPath("temp");
|
|
const tempIndexPath = path.join(tempDir, "timesafari-index.html");
|
|
fs.writeFileSync(tempIndexPath, indexContent);
|
|
|
|
// Load the modified index.html
|
|
mainWindow.loadFile(tempIndexPath).catch((err) => {
|
|
logError("Failed to load via loadFile:", err);
|
|
|
|
// Fallback to direct URL loading
|
|
mainWindow.loadURL(`file://${tempIndexPath}`).catch((err2) => {
|
|
logError("Both loading methods failed:", err2);
|
|
mainWindow.loadURL(
|
|
"data:text/html,<h1>Error: Failed to load TimeSafari</h1><p>Please contact support.</p>",
|
|
);
|
|
});
|
|
});
|
|
} else {
|
|
logError(`Index file not found at: ${indexPath}`);
|
|
mainWindow.loadURL(
|
|
"data:text/html,<h1>Error: Cannot find application</h1><p>index.html not found</p>",
|
|
);
|
|
}
|
|
} catch (err) {
|
|
logError("Failed to load app:", err);
|
|
}
|
|
|
|
// Open DevTools in development
|
|
if (isDev) {
|
|
mainWindow.webContents.openDevTools();
|
|
}
|
|
|
|
mainWindow.on("closed", () => {
|
|
mainWindow = null;
|
|
});
|
|
}
|
|
|
|
// App lifecycle events
|
|
app.whenReady().then(() => {
|
|
logDebug(`Starting TimeSafari v${app.getVersion()}`);
|
|
|
|
// Skip the service worker registration for file:// protocol
|
|
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true";
|
|
|
|
createWindow();
|
|
|
|
app.on("activate", () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
});
|
|
});
|
|
|
|
app.on("window-all-closed", () => {
|
|
if (process.platform !== "darwin") app.quit();
|
|
});
|
|
|
|
// Handle uncaught exceptions
|
|
process.on("uncaughtException", (error) => {
|
|
logError("Uncaught Exception:", error);
|
|
});
|
|
|