Mehdi
Upload electron/main.js with huggingface_hub
0ecac7b verified
Raw
History Blame Contribute Delete
12.8 kB
const { app, BrowserWindow, Menu, ipcMain, dialog, shell } = require('electron');
const path = require('path');
const { spawn } = require('child_process');
const Store = require('electron-store');
const express = require('express');
const cors = require('cors');
const http = require('http');
const socketIo = require('socket.io');
const Docker = require('dockerode');
const fs = require('fs');
const crypto = require('crypto');
// Initialize electron store for settings
const store = new Store();
// Create Express server for backend API
const expressApp = express();
const server = http.createServer(expressApp);
const io = socketIo(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"]
}
});
// Docker client
const docker = new Docker();
// Server port - use 3001 to avoid conflict with Next.js dev server
const SERVER_PORT = process.env.PORT || 3001;
// Keep a global reference of the window object
let mainWindow;
let nextjsProcess;
function createWindow() {
// Create the browser window
mainWindow = new BrowserWindow({
width: 1400,
height: 900,
minWidth: 1200,
minHeight: 700,
icon: path.join(__dirname, '../public/icon.png'),
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
sandbox: false
},
titleBarStyle: 'default',
show: false
});
// Load the Next.js app
const startUrl = process.env.ELECTRON_START_URL || `http://localhost:3000`;
mainWindow.loadURL(startUrl);
// Show window when ready
mainWindow.once('ready-to-show', () => {
mainWindow.show();
// Open DevTools in development
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
});
// Set up menu
setupMenu();
// Emitted when the window is closed
mainWindow.on('closed', () => {
mainWindow = null;
});
// Handle external links
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
return { action: 'deny' };
});
}
function setupMenu() {
const template = [
{
label: 'فایل',
submenu: [
{
label: 'تنظیمات',
accelerator: 'Ctrl+,',
click: () => {
mainWindow.webContents.send('navigate-to-settings');
}
},
{ type: 'separator' },
{
label: 'خروج',
accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q',
click: () => {
app.quit();
}
}
]
},
{
label: 'ویرایش',
submenu: [
{ role: 'undo', label: 'بازگشت' },
{ role: 'redo', label: 'دوباره' },
{ type: 'separator' },
{ role: 'cut', label: 'برش' },
{ role: 'copy', label: 'کپی' },
{ role: 'paste', label: 'چسباندن' }
]
},
{
label: 'دیدن',
submenu: [
{ role: 'reload', label: 'بارگذاری مجدد' },
{ role: 'forceReload', label: 'بارگذاری اجباری' },
{ role: 'toggleDevTools', label: 'ابزار توسعه' },
{ type: 'separator' },
{ role: 'resetZoom', label: 'بازنشانی زوم' },
{ role: 'zoomIn', label: 'بزرگنمایی' },
{ role: 'zoomOut', label: 'کوچکنمایی' },
{ type: 'separator' },
{ role: 'togglefullscreen', label: 'تمام صفحه' }
]
},
{
label: 'کمک',
submenu: [
{
label: 'راهنما',
click: () => {
mainWindow.webContents.send('navigate-to-help');
}
},
{
label: 'درباره',
click: () => {
dialog.showMessageBox(mainWindow, {
type: 'info',
title: 'درباره GhadirSync-AI',
message: 'GhadirSync-AI نسخه 1.0.0',
detail: 'دستیار هوشمند توسعه نرمافزار با پشتیبانی از زبان فارسی\nساخته شده با Electron و Next.js\n\nBuilt with anycoder - https://huggingface.co/spaces/akhaliq/anycoder'
});
}
}
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
// Express API Routes
expressApp.use(cors());
expressApp.use(express.json({ limit: '50mb' }));
expressApp.use(express.urlencoded({ extended: true, limit: '50mb' }));
// API Routes
expressApp.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Model management
expressApp.get('/api/models', (req, res) => {
const modelsDir = path.join(app.getPath('userData'), 'models');
if (!fs.existsSync(modelsDir)) {
fs.mkdirSync(modelsDir, { recursive: true });
}
const models = fs.readdirSync(modelsDir).filter(file =>
file.endsWith('.gguf') || file.endsWith('.onnx') || file.endsWith('.bin')
).map(file => ({
id: crypto.createHash('md5').update(file).digest('hex'),
name: file,
path: path.join(modelsDir, file),
size: fs.statSync(path.join(modelsDir, file)).size,
loaded: false
}));
res.json(models);
});
// Voice processing
expressApp.post('/api/voice/transcribe', async (req, res) => {
try {
const { audioData, language = 'fa' } = req.body;
// Here you would integrate with whisper-node or similar
// For now, return mock response
res.json({
text: language === 'fa' ? 'این یک تست صوتی است' : 'This is a voice test',
language: language,
confidence: 0.95
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Text to speech
expressApp.post('/api/voice/synthesize', async (req, res) => {
try {
const { text, voice = 'default', language = 'fa' } = req.body;
// Here you would integrate with VITS or similar TTS
// For now, return mock response
res.json({
audioUrl: `/api/voice/audio/${Date.now()}.wav`,
duration: text.length * 0.1
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Code execution sandbox
expressApp.post('/api/sandbox/execute', async (req, res) => {
try {
const { code, language, timeout = 30000 } = req.body;
// Create Docker container for sandboxed execution
const container = await docker.createContainer({
Image: language === 'javascript' ? 'node:20-alpine' : 'python:3.11-alpine',
Cmd: language === 'javascript' ? ['node', '-e', code] : ['python', '-c', code],
HostConfig: {
Memory: 512 * 1024 * 1024, // 512MB
CpuShares: 512,
NetworkMode: 'none', // No network access
ReadonlyRootfs: true,
SecurityOpt: ['no-new-privileges:true']
},
WorkingDir: '/sandbox',
OpenStdin: false,
AttachStdin: false,
AttachStdout: true,
AttachStderr: true,
Tty: false
});
await container.start();
const logs = await container.logs({
follow: true,
stdout: true,
stderr: true
});
let output = '';
logs.on('data', (chunk) => {
output += chunk.toString();
});
// Wait for completion or timeout
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Execution timeout')), timeout)
);
const waitPromise = container.wait();
try {
await Promise.race([waitPromise, timeoutPromise]);
} catch (error) {
await container.kill();
throw error;
}
const result = await container.inspect();
await container.remove();
res.json({
output: output,
exitCode: result.State.ExitCode,
success: result.State.ExitCode === 0,
executionTime: result.State.FinishedAt ?
new Date(result.State.FinishedAt).getTime() - new Date(result.State.StartedAt).getTime() : 0
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// File operations
expressApp.post('/api/files/save', (req, res) => {
try {
const { path: filePath, content } = req.body;
const fullPath = path.join(app.getPath('documents'), 'GhadirSync-Projects', filePath);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, content, 'utf8');
res.json({ success: true, path: fullPath });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
expressApp.get('/api/files/read', (req, res) => {
try {
const { path: filePath } = req.query;
const fullPath = path.join(app.getPath('documents'), 'GhadirSync-Projects', filePath);
if (!fs.existsSync(fullPath)) {
return res.status(404).json({ error: 'File not found' });
}
const content = fs.readFileSync(fullPath, 'utf8');
res.json({ content, path: fullPath });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Web browsing sandbox
expressApp.post('/api/web/browse', async (req, res) => {
try {
const { url, method = 'GET', headers = {} } = req.body;
// Validate URL against whitelist
const whitelist = store.get('webWhitelist', ['https://api.github.com', 'https://registry.npmjs.org']);
const urlObj = new URL(url);
if (!whitelist.some(w => url.startsWith(w))) {
return res.status(403).json({ error: 'URL not in whitelist' });
}
const response = await axios({ method, url, headers, timeout: 30000 });
// Log the request for audit
const logsDir = path.join(app.getPath('userData'), 'logs');
fs.mkdirSync(logsDir, { recursive: true });
fs.appendFileSync(
path.join(logsDir, 'web-access.log'),
`${new Date().toISOString()} - ${method} ${url} - ${response.status}\n`
);
res.json({
status: response.status,
data: response.data,
headers: response.headers
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Socket.IO for real-time communication
io.on('connection', (socket) => {
console.log('Client connected:', socket.id);
socket.on('voice-data', (data) => {
// Process voice data in real-time
socket.broadcast.emit('voice-processing', { status: 'processing' });
});
socket.on('agent-task', (task) => {
// Handle agent tasks
io.emit('agent-status', { taskId: task.id, status: 'started' });
});
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id);
});
});
// Initialize Next.js process
function startNextJSServer() {
const isDev = process.env.NODE_ENV === 'development';
const command = isDev ? 'npm' : 'node';
const args = isDev ? ['run', 'next-dev'] : ['node_modules/next/dist/bin/next', 'start'];
nextjsProcess = spawn(command, args, {
cwd: __dirname,
stdio: 'inherit',
shell: true
});
nextjsProcess.on('error', (error) => {
console.error('Failed to start Next.js:', error);
});
nextjsProcess.on('exit', (code) => {
console.log(`Next.js process exited with code ${code}`);
});
}
// IPC handlers
ipcMain.handle('get-app-version', () => app.getVersion());
ipcMain.handle('get-user-data-path', () => app.getPath('userData'));
ipcMain.handle('get-documents-path', () => app.getPath('documents'));
ipcMain.handle('show-open-dialog', (event, options) => dialog.showOpenDialog(mainWindow, options));
ipcMain.handle('show-save-dialog', (event, options) => dialog.showSaveDialog(mainWindow, options));
ipcMain.handle('set-store', (event, key, value) => store.set(key, value));
ipcMain.handle('get-store', (event, key, defaultValue) => store.get(key, defaultValue));
ipcMain.handle('delete-store', (event, key) => store.delete(key));
ipcMain.handle('clear-store', () => store.clear());
// This method will be called when Electron has finished initialization
app.whenReady().then(() => {
createWindow();
startNextJSServer();
server.listen(SERVER_PORT, () => {
console.log(`Backend server running on port ${SERVER_PORT}`);
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
// Quit when all windows are closed
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('before-quit', () => {
if (nextjsProcess) {
nextjsProcess.kill();
}
server.close();
});
// Security: Prevent navigation to external URLs
app.on('web-contents-created', (event, contents) => {
contents.on('will-navigate', (event, navigationUrl) => {
const parsedUrl = new URL(navigationUrl);
if (parsedUrl.origin !== 'http://localhost:3000') {
event.preventDefault();
}
});
});