Update Electron main process for production readiness and app protocol handling
- Register `app://` protocol for secure file handling in production. - Adjust icon paths to support macOS and Windows/Linux distinctions. - Enhance security by validating file paths under the `out/` directory. - Replace `file://` accesses with the `app://` protocol. - Update `package.json` build scripts for platform-specific builds and refined output directory structure. - Modify main and login window settings for improved compatibility across all platforms.
This commit is contained in:
@@ -291,7 +291,7 @@ function ScribeContent() {
|
|||||||
className="bg-background text-text-primary h-screen flex flex-col items-center justify-center font-['Lora']">
|
className="bg-background text-text-primary h-screen flex flex-col items-center justify-center font-['Lora']">
|
||||||
<div className="flex flex-col items-center space-y-6">
|
<div className="flex flex-col items-center space-y-6">
|
||||||
<div className="animate-pulse">
|
<div className="animate-pulse">
|
||||||
<img src="/logo.png" alt="ERitors Logo" style={{width: 400, height: 400}} />
|
<img src="/eritors-favicon-white.png" alt="ERitors Logo" style={{width: 400, height: 400}} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<div className="w-2 h-2 bg-primary rounded-full animate-bounce"></div>
|
<div className="w-2 h-2 bg-primary rounded-full animate-bounce"></div>
|
||||||
|
|||||||
101
electron/main.ts
101
electron/main.ts
@@ -1,14 +1,30 @@
|
|||||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
import { app, BrowserWindow, ipcMain, nativeImage, protocol } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
// Fix pour __dirname en ES modules
|
// Fix pour __dirname en ES modules
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = !app.isPackaged;
|
||||||
|
|
||||||
|
// Enregistrer le protocole app:// comme standard (avant app.whenReady)
|
||||||
|
if (!isDev) {
|
||||||
|
protocol.registerSchemesAsPrivileged([
|
||||||
|
{
|
||||||
|
scheme: 'app',
|
||||||
|
privileges: {
|
||||||
|
standard: true,
|
||||||
|
secure: true,
|
||||||
|
supportFetchAPI: true,
|
||||||
|
corsEnabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// Définir le nom de l'application
|
// Définir le nom de l'application
|
||||||
app.setName('ERitors Scribe');
|
app.setName('ERitors Scribe');
|
||||||
@@ -21,7 +37,9 @@ const preloadPath = isDev
|
|||||||
// Icône de l'application
|
// Icône de l'application
|
||||||
const iconPath = isDev
|
const iconPath = isDev
|
||||||
? path.join(__dirname, '../build/icon.png')
|
? path.join(__dirname, '../build/icon.png')
|
||||||
: path.join(__dirname, '../build/icon.png');
|
: process.platform === 'darwin'
|
||||||
|
? path.join(process.resourcesPath, 'icon.icns') // macOS utilise .icns
|
||||||
|
: path.join(process.resourcesPath, 'app.asar/build/icon.png'); // Windows/Linux utilisent .png
|
||||||
|
|
||||||
// Store sécurisé pour le token
|
// Store sécurisé pour le token
|
||||||
const store = new Store({
|
const store = new Store({
|
||||||
@@ -36,7 +54,8 @@ function createLoginWindow(): void {
|
|||||||
width: 500,
|
width: 500,
|
||||||
height: 900,
|
height: 900,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
icon: iconPath,
|
// Ne pas définir icon sur macOS - utilise l'icône de l'app bundle
|
||||||
|
...(process.platform !== 'darwin' && { icon: iconPath }),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
@@ -52,13 +71,7 @@ function createLoginWindow(): void {
|
|||||||
loginWindow.loadURL(`http://localhost:${devPort}/login/login`);
|
loginWindow.loadURL(`http://localhost:${devPort}/login/login`);
|
||||||
loginWindow.webContents.openDevTools();
|
loginWindow.webContents.openDevTools();
|
||||||
} else {
|
} else {
|
||||||
loginWindow.loadURL(
|
loginWindow.loadURL('app://./login/login/index.html');
|
||||||
url.format({
|
|
||||||
pathname: path.join(__dirname, '../out/login/login/index.html'),
|
|
||||||
protocol: 'file:',
|
|
||||||
slashes: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loginWindow.once('ready-to-show', () => {
|
loginWindow.once('ready-to-show', () => {
|
||||||
@@ -74,7 +87,8 @@ function createMainWindow(): void {
|
|||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 800,
|
height: 800,
|
||||||
icon: iconPath,
|
// Ne pas définir icon sur macOS - utilise l'icône de l'app bundle
|
||||||
|
...(process.platform !== 'darwin' && { icon: iconPath }),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
@@ -89,13 +103,7 @@ function createMainWindow(): void {
|
|||||||
mainWindow.loadURL(`http://localhost:${devPort}`);
|
mainWindow.loadURL(`http://localhost:${devPort}`);
|
||||||
mainWindow.webContents.openDevTools();
|
mainWindow.webContents.openDevTools();
|
||||||
} else {
|
} else {
|
||||||
mainWindow.loadURL(
|
mainWindow.loadURL('app://./index.html');
|
||||||
url.format({
|
|
||||||
pathname: path.join(__dirname, '../out/index.html'),
|
|
||||||
protocol: 'file:',
|
|
||||||
slashes: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mainWindow.once('ready-to-show', () => {
|
mainWindow.once('ready-to-show', () => {
|
||||||
@@ -143,13 +151,61 @@ ipcMain.on('logout', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
|
console.log('App ready, isDev:', isDev);
|
||||||
|
console.log('resourcesPath:', process.resourcesPath);
|
||||||
|
console.log('isPackaged:', app.isPackaged);
|
||||||
|
|
||||||
|
// Enregistrer le protocole custom app:// pour servir les fichiers depuis out/
|
||||||
|
if (!isDev) {
|
||||||
|
const outPath = path.join(process.resourcesPath, 'app.asar.unpacked/out');
|
||||||
|
|
||||||
|
protocol.handle('app', async (request) => {
|
||||||
|
// Enlever app:// et ./
|
||||||
|
let filePath = request.url.replace('app://', '').replace(/^\.\//, '');
|
||||||
|
const fullPath = path.normalize(path.join(outPath, filePath));
|
||||||
|
|
||||||
|
// Vérifier que le chemin est bien dans out/ (sécurité)
|
||||||
|
if (!fullPath.startsWith(outPath)) {
|
||||||
|
console.error('Security: Attempted to access file outside out/:', fullPath);
|
||||||
|
return new Response('Forbidden', { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await fs.promises.readFile(fullPath);
|
||||||
|
const ext = path.extname(fullPath).toLowerCase();
|
||||||
|
const mimeTypes: Record<string, string> = {
|
||||||
|
'.html': 'text/html',
|
||||||
|
'.css': 'text/css',
|
||||||
|
'.js': 'application/javascript',
|
||||||
|
'.json': 'application/json',
|
||||||
|
'.png': 'image/png',
|
||||||
|
'.jpg': 'image/jpeg',
|
||||||
|
'.svg': 'image/svg+xml',
|
||||||
|
'.ico': 'image/x-icon',
|
||||||
|
'.woff': 'font/woff',
|
||||||
|
'.woff2': 'font/woff2',
|
||||||
|
'.ttf': 'font/ttf',
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Response(data, {
|
||||||
|
headers: { 'Content-Type': mimeTypes[ext] || 'application/octet-stream' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load:', fullPath, error);
|
||||||
|
return new Response('Not found', { status: 404 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Définir l'icône du Dock sur macOS
|
// Définir l'icône du Dock sur macOS
|
||||||
if (process.platform === 'darwin' && app.dock) {
|
if (process.platform === 'darwin' && app.dock) {
|
||||||
app.dock.setIcon(iconPath);
|
const icon = nativeImage.createFromPath(iconPath);
|
||||||
|
app.dock.setIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vérifier si un token existe
|
// Vérifier si un token existe
|
||||||
const token = store.get('authToken');
|
const token = store.get('authToken');
|
||||||
|
console.log('Token exists:', !!token);
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
// Token existe, ouvrir la fenêtre principale
|
// Token existe, ouvrir la fenêtre principale
|
||||||
@@ -172,7 +228,6 @@ app.whenReady().then(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (process.platform !== 'darwin') {
|
// Quitter l'application quand toutes les fenêtres sont fermées
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
27
package.json
27
package.json
@@ -5,17 +5,11 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/electron/main.js",
|
"main": "dist/electron/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:next": "next dev -p 4000",
|
"dev": "concurrently \"next dev -p 4000\" \"wait-on http://localhost:4000 && electron -r tsx/cjs electron/main.ts\"",
|
||||||
"dev:electron": "NODE_ENV=development PORT=4000 electron -r tsx/cjs electron/main.ts",
|
"build:mac": "next build && tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json && electron-builder build --mac",
|
||||||
"dev": "concurrently \"npm run dev:next\" \"wait-on http://localhost:4000 && npm run dev:electron\"",
|
"build:win": "next build && tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json && electron-builder build --win",
|
||||||
"build:next": "next build",
|
"build:linux": "next build && tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json && electron-builder build --linux",
|
||||||
"build:electron": "tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json",
|
"build:all": "next build && tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json && electron-builder build --mac --win --linux"
|
||||||
"build": "npm run build:next && npm run build:electron",
|
|
||||||
"start": "electron .",
|
|
||||||
"package": "npm run build && electron-builder build --mac --win --linux",
|
|
||||||
"package:mac": "npm run build && electron-builder build --mac",
|
|
||||||
"package:win": "npm run build && electron-builder build --win",
|
|
||||||
"package:linux": "npm run build && electron-builder build --linux"
|
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -71,17 +65,22 @@
|
|||||||
"tailwindcss": "^4.1.17"
|
"tailwindcss": "^4.1.17"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.eritorsscribe.app",
|
"appId": "com.eritors.scribe.desktop",
|
||||||
"productName": "EritorsScribe",
|
"productName": "ERitors Scribe",
|
||||||
"files": [
|
"files": [
|
||||||
"dist/**/*",
|
"dist/**/*",
|
||||||
"out/**/*",
|
"out/**/*",
|
||||||
|
"build/**/*",
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
|
"asarUnpack": [
|
||||||
|
"out/**/*"
|
||||||
|
],
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "release"
|
"output": "release"
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
|
"icon": "build/icons/mac/icon.icns",
|
||||||
"target": [
|
"target": [
|
||||||
"dmg",
|
"dmg",
|
||||||
"zip"
|
"zip"
|
||||||
@@ -93,6 +92,7 @@
|
|||||||
"entitlementsInherit": "build/entitlements.mac.plist"
|
"entitlementsInherit": "build/entitlements.mac.plist"
|
||||||
},
|
},
|
||||||
"win": {
|
"win": {
|
||||||
|
"icon": "build/icons/win/icon.ico",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
@@ -104,6 +104,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
|
"icon": "build/icons/png",
|
||||||
"target": [
|
"target": [
|
||||||
"AppImage",
|
"AppImage",
|
||||||
"deb"
|
"deb"
|
||||||
|
|||||||
Reference in New Issue
Block a user