feat: update Electron configuration and static server implementation

- Upgraded Electron version to 39.2.7 and TypeScript to 5.9.3 in package-lock.json.
- Modified next.config.ts to set output to "export" for static site generation.
- Changed package.json to include the output directory for deployment.
- Enhanced main.js to implement a static file server for production builds, serving files from the "out" directory.
- Adjusted the loading mechanism to use the static server in production and the Next.js dev server in development.
This commit is contained in:
Kacper
2025-12-13 23:12:10 +01:00
parent bc46a18372
commit bea115d1e4
4 changed files with 935 additions and 647 deletions

View File

@@ -8,15 +8,23 @@
const path = require("path");
const { spawn } = require("child_process");
const fs = require("fs");
// Load environment variables from .env file
require("dotenv").config({ path: path.join(__dirname, "../.env") });
const http = require("http");
const { app, BrowserWindow, ipcMain, dialog, shell } = require("electron");
// Load environment variables from .env file (development only)
if (!app.isPackaged) {
try {
require("dotenv").config({ path: path.join(__dirname, "../.env") });
} catch (error) {
console.warn("[Electron] dotenv not available:", error.message);
}
}
let mainWindow = null;
let serverProcess = null;
let staticServer = null;
const SERVER_PORT = 3008;
const STATIC_PORT = 3007;
// Get icon path - works in both dev and production
function getIconPath() {
@@ -25,6 +33,67 @@ function getIconPath() {
: path.join(__dirname, "../public/logo.png");
}
/**
* Start static file server for production builds
*/
function startStaticServer() {
const staticPath = path.join(__dirname, "../out");
staticServer = http.createServer((request, response) => {
// Parse the URL and remove query string
let filePath = path.join(staticPath, request.url.split("?")[0]);
// Default to index.html for directory requests
if (filePath.endsWith("/")) {
filePath = path.join(filePath, "index.html");
} else if (!path.extname(filePath)) {
filePath += ".html";
}
// Check if file exists
fs.stat(filePath, (err, stats) => {
if (err || !stats.isFile()) {
// Try index.html for SPA fallback
filePath = path.join(staticPath, "index.html");
}
// Read and serve the file
fs.readFile(filePath, (error, content) => {
if (error) {
response.writeHead(500);
response.end("Server Error");
return;
}
// Set content type based on file extension
const ext = path.extname(filePath);
const contentTypes = {
".html": "text/html",
".js": "application/javascript",
".css": "text/css",
".json": "application/json",
".png": "image/png",
".jpg": "image/jpeg",
".gif": "image/gif",
".svg": "image/svg+xml",
".ico": "image/x-icon",
".woff": "font/woff",
".woff2": "font/woff2",
".ttf": "font/ttf",
".eot": "application/vnd.ms-fontobject",
};
response.writeHead(200, { "Content-Type": contentTypes[ext] || "application/octet-stream" });
response.end(content);
});
});
});
staticServer.listen(STATIC_PORT, () => {
console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`);
});
}
/**
* Start the backend server
*/
@@ -165,15 +234,11 @@ function createWindow() {
backgroundColor: "#0a0a0a",
});
// Load Next.js dev server in development or production build
// Load Next.js dev server in development or static server in production
const isDev = !app.isPackaged;
if (isDev) {
mainWindow.loadURL("http://localhost:3007");
if (process.env.OPEN_DEVTOOLS === "true") {
mainWindow.webContents.openDevTools();
}
} else {
mainWindow.loadFile(path.join(__dirname, "../.next/server/app/index.html"));
mainWindow.loadURL(`http://localhost:${STATIC_PORT}`);
if (isDev && process.env.OPEN_DEVTOOLS === "true") {
mainWindow.webContents.openDevTools();
}
mainWindow.on("closed", () => {
@@ -195,6 +260,11 @@ app.whenReady().then(async () => {
}
try {
// Start static file server in production
if (app.isPackaged) {
startStaticServer();
}
// Start backend server
await startServer();
@@ -225,6 +295,13 @@ app.on("before-quit", () => {
serverProcess.kill();
serverProcess = null;
}
// Close static server
if (staticServer) {
console.log("[Electron] Stopping static server...");
staticServer.close();
staticServer = null;
}
});
// ============================================

View File

@@ -1,6 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
env: {
CLAUDE_CODE_OAUTH_TOKEN: process.env.CLAUDE_CODE_OAUTH_TOKEN || "",
},

View File

@@ -100,7 +100,7 @@
},
"files": [
"electron/**/*",
".next/**/*",
"out/**/*",
"public/**/*",
"!node_modules/**/*"
],

1478
package-lock.json generated

File diff suppressed because it is too large Load Diff