v1.6.0 #221
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Read(/C:\\Users\\29037\\WebstormProjects\\Termix\\docker/**)",
|
|
||||||
"Bash(git fetch:*)",
|
|
||||||
"Bash(git pull:*)",
|
|
||||||
"Bash(git checkout:*)",
|
|
||||||
"Bash(git add:*)",
|
|
||||||
"Bash(grep:*)",
|
|
||||||
"Bash(git push:*)",
|
|
||||||
"Bash(git branch:*)",
|
|
||||||
"Bash(npm run build:*)",
|
|
||||||
"Bash(npm install)",
|
|
||||||
"Bash(npm run electron:build:*)",
|
|
||||||
"Bash(npm uninstall:*)",
|
|
||||||
"Bash(git remote set-url:*)",
|
|
||||||
"Bash(npm run dev:backend:*)",
|
|
||||||
"Bash(taskkill:*)",
|
|
||||||
"Bash(node:*)",
|
|
||||||
"WebFetch(domain:ui.shadcn.com)"
|
|
||||||
],
|
|
||||||
"deny": [],
|
|
||||||
"ask": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,3 +24,4 @@ dist-ssr
|
|||||||
*.sw?
|
*.sw?
|
||||||
/db/
|
/db/
|
||||||
/release/
|
/release/
|
||||||
|
/.claude/
|
||||||
|
|||||||
35
build-electron.js
Normal file
35
build-electron.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { execSync } from 'child_process';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
// Check if we're in a path with spaces
|
||||||
|
const currentPath = process.cwd();
|
||||||
|
if (currentPath.includes(' ')) {
|
||||||
|
console.log('⚠️ Warning: Project path contains spaces which may cause issues with native modules.');
|
||||||
|
console.log('Current path:', currentPath);
|
||||||
|
console.log('Consider moving the project to a path without spaces for better compatibility.');
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set environment variables to help with native module compilation
|
||||||
|
process.env.npm_config_cache = path.join(process.cwd(), 'node_modules', '.cache');
|
||||||
|
process.env.npm_config_tmp = path.join(process.cwd(), 'node_modules', '.tmp');
|
||||||
|
|
||||||
|
console.log('Building Electron application...');
|
||||||
|
|
||||||
|
// Skip better-sqlite3 rebuild due to path issues
|
||||||
|
console.log('Skipping better-sqlite3 rebuild due to path space issues...');
|
||||||
|
console.log('Note: Using existing better-sqlite3 installation.');
|
||||||
|
|
||||||
|
// Run the electron-builder
|
||||||
|
try {
|
||||||
|
execSync('npx electron-builder', { stdio: 'inherit' });
|
||||||
|
console.log('✅ Electron build completed successfully!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Electron build failed:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@@ -6,18 +6,23 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist/**/*",
|
"dist/**/*",
|
||||||
"electron/**/*"
|
"electron/**/*",
|
||||||
|
"public/**/*",
|
||||||
|
"!**/node_modules/**/*",
|
||||||
|
"!src/**/*",
|
||||||
|
"!*.md",
|
||||||
|
"!tsconfig*.json",
|
||||||
|
"!vite.config.ts",
|
||||||
|
"!eslint.config.js"
|
||||||
],
|
],
|
||||||
"extraMetadata": {
|
"extraMetadata": {
|
||||||
"main": "electron/main-simple.cjs"
|
"main": "electron/main-simple.cjs"
|
||||||
},
|
},
|
||||||
|
"buildDependenciesFromSource": false,
|
||||||
|
"nodeGypRebuild": false,
|
||||||
|
"npmRebuild": false,
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "public.app-category.developer-tools",
|
"category": "public.app-category.developer-tools",
|
||||||
"icon": "public/icon.icns",
|
|
||||||
"hardenedRuntime": true,
|
|
||||||
"gatekeeperAssess": false,
|
|
||||||
"entitlements": "build/entitlements.mac.plist",
|
|
||||||
"entitlementsInherit": "build/entitlements.mac.plist",
|
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "dmg",
|
"target": "dmg",
|
||||||
@@ -31,7 +36,7 @@
|
|||||||
},
|
},
|
||||||
"win": {
|
"win": {
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
"icon": "public/icon.ico"
|
"forceCodeSigning": false
|
||||||
},
|
},
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"oneClick": false,
|
"oneClick": false,
|
||||||
|
|||||||
@@ -16,27 +16,68 @@ function startBackendServer() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const backendPath = path.join(__dirname, '../dist/backend/starter.js');
|
// 在打包环境中,后端文件在 resources/app/dist/backend/backend/ 目录下
|
||||||
|
const backendPath = isDev
|
||||||
|
? path.join(__dirname, '../dist/backend/starter.js')
|
||||||
|
: path.join(process.resourcesPath, 'app', 'dist', 'backend', 'backend', 'starter.js');
|
||||||
|
|
||||||
console.log('Starting backend server from:', backendPath);
|
console.log('Starting backend server from:', backendPath);
|
||||||
|
console.log('Working directory:', process.cwd());
|
||||||
|
|
||||||
|
// 设置环境变量
|
||||||
|
const env = {
|
||||||
|
...process.env,
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
DATA_PATH: app.getPath('userData'),
|
||||||
|
DB_PATH: path.join(app.getPath('userData'), 'database.db'),
|
||||||
|
VERSION: app.getVersion()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
const fs = require('fs');
|
||||||
|
if (!fs.existsSync(backendPath)) {
|
||||||
|
console.error('Backend file not found at:', backendPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Backend file exists, starting process...');
|
||||||
|
console.log('Environment variables:', env);
|
||||||
|
|
||||||
backendProcess = spawn('node', [backendPath], {
|
backendProcess = spawn('node', [backendPath], {
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
detached: false,
|
detached: false,
|
||||||
cwd: path.join(__dirname, '..') // Set working directory to app root
|
cwd: isDev ? path.join(__dirname, '..') : process.resourcesPath,
|
||||||
|
env: env
|
||||||
});
|
});
|
||||||
|
|
||||||
backendProcess.stdout.on('data', (data) => {
|
backendProcess.stdout.on('data', (data) => {
|
||||||
console.log('Backend:', data.toString());
|
console.log('Backend stdout:', data.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
backendProcess.stderr.on('data', (data) => {
|
backendProcess.stderr.on('data', (data) => {
|
||||||
console.error('Backend Error:', data.toString());
|
console.error('Backend stderr:', data.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
backendProcess.on('close', (code) => {
|
backendProcess.on('close', (code) => {
|
||||||
console.log(`Backend process exited with code ${code}`);
|
console.log(`Backend process exited with code ${code}`);
|
||||||
backendProcess = null;
|
backendProcess = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
backendProcess.on('error', (error) => {
|
||||||
|
console.error('Failed to start backend process:', error);
|
||||||
|
console.error('Error details:', error.message);
|
||||||
|
console.error('Error code:', error.code);
|
||||||
|
backendProcess = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 等待一下看看进程是否启动成功
|
||||||
|
setTimeout(() => {
|
||||||
|
if (backendProcess && !backendProcess.killed) {
|
||||||
|
console.log('Backend process appears to be running');
|
||||||
|
} else {
|
||||||
|
console.error('Backend process failed to start or died immediately');
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停止后端服务
|
// 停止后端服务
|
||||||
@@ -144,12 +185,29 @@ ipcMain.handle('get-platform', () => {
|
|||||||
return process.platform;
|
return process.platform;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 应用事件处理
|
ipcMain.handle('get-backend-port', () => {
|
||||||
app.whenReady().then(() => {
|
return 8081; // 后端服务端口
|
||||||
// 在生产环境启动后端服务
|
});
|
||||||
if (!isDev) {
|
|
||||||
|
ipcMain.handle('restart-backend', async () => {
|
||||||
|
try {
|
||||||
|
stopBackendServer();
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
|
||||||
startBackendServer();
|
startBackendServer();
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 应用事件处理
|
||||||
|
app.whenReady().then(async () => {
|
||||||
|
// 启动后端服务
|
||||||
|
startBackendServer();
|
||||||
|
|
||||||
|
// 等待后端服务启动
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
|
||||||
createWindow();
|
createWindow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
// 获取平台信息
|
// 获取平台信息
|
||||||
getPlatform: () => ipcRenderer.invoke('get-platform'),
|
getPlatform: () => ipcRenderer.invoke('get-platform'),
|
||||||
|
|
||||||
|
// 获取后端端口
|
||||||
|
getBackendPort: () => ipcRenderer.invoke('get-backend-port'),
|
||||||
|
|
||||||
|
// 重启后端服务
|
||||||
|
restartBackend: () => ipcRenderer.invoke('restart-backend'),
|
||||||
|
|
||||||
// 环境检测
|
// 环境检测
|
||||||
isElectron: true,
|
isElectron: true,
|
||||||
isDev: process.env.NODE_ENV === 'development',
|
isDev: process.env.NODE_ENV === 'development',
|
||||||
|
|||||||
5449
package-lock.json
generated
5449
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -1,7 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "termix",
|
"name": "termix",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "1.6.0",
|
||||||
|
"description": "A web-based server management platform with SSH terminal, tunneling, and file editing capabilities",
|
||||||
|
"author": "Karmaa",
|
||||||
|
"main": "electron/main-simple.cjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -9,7 +12,12 @@
|
|||||||
"build:backend": "tsc -p tsconfig.node.json",
|
"build:backend": "tsc -p tsconfig.node.json",
|
||||||
"dev:backend": "tsc -p tsconfig.node.json && node ./dist/backend/backend/starter.js",
|
"dev:backend": "tsc -p tsconfig.node.json && node ./dist/backend/backend/starter.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"electron": "electron .",
|
||||||
|
"electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:5173 && electron .\"",
|
||||||
|
"electron:build": "npm run build && npm run build:backend && electron-builder --dir",
|
||||||
|
"electron:build-win": "npm run build && npm run build:backend && electron-builder --win --dir",
|
||||||
|
"electron:pack": "npm run build && npm run build:backend && electron-packager . Termix --platform=all --arch=x64,arm64 --out=release --overwrite"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^5.1.1",
|
"@hookform/resolvers": "^5.1.1",
|
||||||
@@ -97,6 +105,10 @@
|
|||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
"@vitejs/plugin-react-swc": "^3.10.2",
|
"@vitejs/plugin-react-swc": "^3.10.2",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
|
"concurrently": "^9.2.1",
|
||||||
|
"electron": "^38.0.0",
|
||||||
|
"electron-builder": "^26.0.12",
|
||||||
|
"electron-packager": "^17.1.2",
|
||||||
"eslint": "^9.34.0",
|
"eslint": "^9.34.0",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
@@ -105,6 +117,7 @@
|
|||||||
"tw-animate-css": "^1.3.5",
|
"tw-animate-css": "^1.3.5",
|
||||||
"typescript": "~5.9.2",
|
"typescript": "~5.9.2",
|
||||||
"typescript-eslint": "^8.40.0",
|
"typescript-eslint": "^8.40.0",
|
||||||
"vite": "^7.1.5"
|
"vite": "^7.1.5",
|
||||||
|
"wait-on": "^8.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -537,13 +537,12 @@ export function LeftSidebar({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="flex-1"
|
className="flex-1 cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDeleteAccountOpen(false);
|
setDeleteAccountOpen(false);
|
||||||
setDeletePassword("");
|
setDeletePassword("");
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
}}
|
}}
|
||||||
className="cursor-pointer"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export const Terminal = forwardRef<any, SSHTerminalProps>(function SSHTerminal(
|
|||||||
scrollback: 10000,
|
scrollback: 10000,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontFamily: '"JetBrains Mono Nerd Font", "MesloLGS NF", "FiraCode Nerd Font", "Cascadia Code", "JetBrains Mono", Consolas, "Courier New", monospace',
|
fontFamily: '"JetBrains Mono Nerd Font", "MesloLGS NF", "FiraCode Nerd Font", "Cascadia Code", "JetBrains Mono", Consolas, "Courier New", monospace',
|
||||||
theme: {background: 'var(--color-dark-bg-darkest)', foreground: '#f7f7f7'},
|
theme: {background: '#09090b', foreground: '#f7f7f7'},
|
||||||
allowTransparency: true,
|
allowTransparency: true,
|
||||||
convertEol: true,
|
convertEol: true,
|
||||||
windowsMode: false,
|
windowsMode: false,
|
||||||
|
|||||||
@@ -742,7 +742,7 @@ export function HomepageAuth({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="password">{t('common.password')}</Label>
|
<Label htmlFor="password">{t('common.password')}</Label>
|
||||||
<PasswordInput id="password" required className="h-55 text-base"
|
<PasswordInput id="password" required className="h-11 text-base"
|
||||||
value={password} onChange={e => setPassword(e.target.value)}
|
value={password} onChange={e => setPassword(e.target.value)}
|
||||||
disabled={loading || internalLoggedIn}/>
|
disabled={loading || internalLoggedIn}/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user