У меня есть приложение Baisc Todo с полной функциональностью CRUD и подключен к следующему маршрутизатору JS App с маршрутом API. И я попытался вмешиваться в электрон JS. Итак, я сделал < /p>
in package.json < /p>
Скрипт < /p>
const { app, BrowserWindow, Menu } = require("electron")
const path = require("path")
const { spawn } = require("child_process")
// Detect if we're in development mode
const isDev = process.env.NODE_ENV === "development" || !app.isPackaged
let mainWindow
let nextProcess
let nextPort = 3000 // Default port
async function createWindow() {
console.log("🎉 Electron app is ready!")
console.log("Creating Electron window...")
console.log("Development mode:", isDev)
// Create the browser window with better security settings
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
webSecurity: true,
allowRunningInsecureContent: false,
experimentalFeatures: false,
},
show: false,
titleBarStyle: "default",
})
// Set up the menu
const template = [
{
label: "File",
submenu: [
{
label: "Quit",
accelerator: process.platform === "darwin" ? "Cmd+Q" : "Ctrl+Q",
click: () => {
app.quit()
},
},
],
},
{
label: "Edit",
submenu: [
{ role: "undo" },
{ role: "redo" },
{ type: "separator" },
{ role: "cut" },
{ role: "copy" },
{ role: "paste" },
],
},
{
label: "View",
submenu: [
{ role: "reload" },
{ role: "forceReload" },
{ role: "toggleDevTools" },
{ type: "separator" },
{ role: "resetZoom" },
{ role: "zoomIn" },
{ role: "zoomOut" },
{ type: "separator" },
{ role: "togglefullscreen" },
],
},
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
if (isDev) {
console.log("Starting in development mode...")
// Start Next.js dev server and get the port
await startNextDev()
// Wait for Next.js to be ready on the detected port
console.log("Waiting for Next.js to be ready...")
await waitForNextJs()
const nextUrl = `http://localhost:${nextPort}`
console.log(`Loading ${nextUrl}`)
mainWindow.loadURL(nextUrl)
} else {
// Production mode
const startUrl = `file://${path.join(__dirname, "../out/index.html")}`
console.log("Loading production build:", startUrl)
mainWindow.loadURL(startUrl)
}
// Show window when ready
mainWindow.once("ready-to-show", () => {
console.log("✅ Todo Desktop App is ready!")
mainWindow.show()
})
// Handle load failures
mainWindow.webContents.on("did-fail-load", (event, errorCode, errorDescription, validatedURL) => {
console.error(`❌ Failed to load ${validatedURL}: ${errorDescription}`)
showErrorPage(errorDescription, validatedURL)
})
// Handle external links
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
require("electron").shell.openExternal(url)
return { action: "deny" }
})
mainWindow.on("closed", () => {
mainWindow = null
})
}
function showErrorPage(errorDescription, validatedURL) {
const errorHtml = `
Todo App - Loading Error
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
text-align: center;
padding: 50px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
}
.container {
background: rgba(255,255,255,0.1);
padding: 40px;
border-radius: 10px;
backdrop-filter: blur(10px);
max-width: 500px;
margin: 0 auto;
}
h1 { color: #ff6b6b; margin-bottom: 20px; }
button {
padding: 12px 24px;
background: #4ecdc4;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
margin: 10px;
}
button:hover { background: #45b7aa; }
.details { font-size: 14px; opacity: 0.8; margin-top: 20px; }
🚫 Loading Error
Failed to load the Todo application.
🔄 Retry
[b]Error:[/b] ${errorDescription}
[b]URL:[/b] ${validatedURL}
[b]Troubleshooting:[/b]
1. Make sure Next.js is running
2. Check if the port is available
3. Try restarting the application
`
mainWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(errorHtml)}`)
}
function startNextDev() {
return new Promise((resolve) => {
console.log("🚀 Starting Next.js dev server...")
const isWindows = process.platform === "win32"
const command = isWindows ? "npm.cmd" : "npm"
nextProcess = spawn(command, ["run", "dev"], {
cwd: path.join(__dirname, ".."),
stdio: ["ignore", "pipe", "pipe"],
shell: isWindows,
})
nextProcess.stdout.on("data", (data) => {
const output = data.toString()
console.log("Next.js:", output.trim())
// Extract port from Next.js output
const portMatch = output.match(/localhost:(\d+)/)
if (portMatch) {
nextPort = Number.parseInt(portMatch[1])
console.log(`📍 Detected Next.js port: ${nextPort}`)
}
if (output.includes("Ready") || output.includes("Local:") || output.includes("started server")) {
console.log("✅ Next.js server is ready!")
resolve()
}
})
nextProcess.stderr.on("data", (data) => {
const error = data.toString()
if (!error.includes("warn")) {
console.error("Next.js Error:", error.trim())
}
})
nextProcess.on("error", (err) => {
console.error("❌ Failed to start Next.js:", err)
resolve()
})
setTimeout(() => {
console.log("⏰ Next.js startup timeout - proceeding...")
resolve()
}, 20000) // Increased timeout
})
}
async function waitForNextJs() {
const maxAttempts = 30
const delay = 1000
for (let i = 0; i < maxAttempts; i++) {
try {
const http = require("http")
await new Promise((resolve, reject) => {
const req = http.get(`http://localhost:${nextPort}`, (res) => {
if (res.statusCode === 200) {
console.log("✅ Next.js is responding!")
resolve()
} else {
reject(new Error(`Status: ${res.statusCode}`))
}
})
req.on("error", reject)
req.setTimeout(3000, () => {
req.destroy()
reject(new Error("Timeout"))
})
})
return // Success!
} catch (error) {
console.log(`⏳ Waiting for Next.js on port ${nextPort}... (${i + 1}/${maxAttempts})`)
await new Promise((resolve) => setTimeout(resolve, delay))
}
}
console.warn("⚠️ Next.js may not be ready, but proceeding anyway")
}
// App event listeners
app.whenReady().then(() => {
createWindow()
})
app.on("window-all-closed", () => {
console.log("👋 All windows closed")
if (nextProcess) {
console.log("🔄 Killing Next.js process...")
nextProcess.kill()
}
if (process.platform !== "darwin") {
app.quit()
}
})
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
app.on("before-quit", () => {
if (nextProcess) {
console.log("🧹 Cleaning up Next.js process...")
nextProcess.kill()
}
})
// Suppress security warnings in development
if (isDev) {
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true"
}
< /code>
, и я также добавил это в мой next.config.ts < /p>
const nextConfig: NextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
typescript: {
ignoreBuildErrors: true,
},
images: {
unoptimized: true,
},
// Remove static export for development to keep API routes working
...(process.env.NODE_ENV === "production" && {
output: "export",
trailingSlash: true,
assetPrefix: "./",
}),
};
Так что теперь, когда я запускаю это с помощью NPM запустить электрон , чтобы проверить его, он работает более или менее. Но когда я попытался создать пакет продукции с помощью NPM запустить Dist , чтобы построить электрон, именно тогда я сталкиваюсь с проблемой. Первый даже не строит его, во -вторых, даже если это произошло, следующая сборка не выходит на маршруты API. Так есть ли способ использовать следующий маршрут приложений JS 15 с API, в электрон JS?
У меня есть приложение Baisc Todo с полной функциональностью CRUD и подключен к следующему маршрутизатору JS App с маршрутом API. И я попытался вмешиваться в электрон JS. Итак, я сделал < /p> in package.json < /p> Скрипт < /p> [code] "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "electron": "cross-env ELECTRON_DISABLE_SECURITY_WARNINGS=true electron .", "electron-dev": "cross-env NODE_ENV=development ELECTRON_DISABLE_SECURITY_WARNINGS=true electron .", "electron-win": "set NODE_ENV=development&& set ELECTRON_DISABLE_SECURITY_WARNINGS=true&& electron .", "build-electron": "next build && next export && electron-builder", "dist": "next build && next export && electron-builder --publish=never", "clean": "taskkill /f /im node.exe 2>nul & rimraf .next out dist", "clean-safe": "rimraf out dist", "kill-port": "npx kill-port 3000 3001" }, < /code> Затем я добавил сборку < /p> "build": { "appId": "com.yourcompany.todoapp", "productName": "Todo Desktop App", "directories": { "output": "dist", "buildResources": "assets" }, "files": [ "out/**/*", "electron/**/*", ".next/**/*", "public/**/*", "node_modules/**/*" ], "mac": { "category": "public.app-category.productivity" }, "win": { "target": "nsis" }, "linux": { "target": "AppImage" }, "extraResources": [ { "from": ".next", "to": "app/.next" } ] }, [/code] Итак, я сделал электрон/main.js [code]const { app, BrowserWindow, Menu } = require("electron") const path = require("path") const { spawn } = require("child_process")
// Detect if we're in development mode const isDev = process.env.NODE_ENV === "development" || !app.isPackaged
let mainWindow let nextProcess let nextPort = 3000 // Default port
async function createWindow() { console.log("🎉 Electron app is ready!") console.log("Creating Electron window...") console.log("Development mode:", isDev)
app.on("before-quit", () => { if (nextProcess) { console.log("🧹 Cleaning up Next.js process...") nextProcess.kill() } })
// Suppress security warnings in development if (isDev) { process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true" } < /code> , и я также добавил это в мой next.config.ts < /p> const nextConfig: NextConfig = { eslint: { ignoreDuringBuilds: true, }, typescript: { ignoreBuildErrors: true, }, images: { unoptimized: true, }, // Remove static export for development to keep API routes working ...(process.env.NODE_ENV === "production" && { output: "export", trailingSlash: true, assetPrefix: "./", }), }; [/code] Так что теперь, когда я запускаю это с помощью NPM запустить электрон , чтобы проверить его, он работает более или менее. Но когда я попытался создать пакет продукции с помощью NPM запустить Dist , чтобы построить электрон, именно тогда я сталкиваюсь с проблемой. Первый даже не строит его, во -вторых, даже если это произошло, следующая сборка не выходит на маршруты API. Так есть ли способ использовать следующий маршрут приложений JS 15 с API, в электрон JS?