diff --git a/AppImage/public/favicon.ico b/AppImage/public/favicon.ico new file mode 100644 index 0000000..35feec3 Binary files /dev/null and b/AppImage/public/favicon.ico differ diff --git a/AppImage/scripts/build_appimage.sh b/AppImage/scripts/build_appimage.sh index e65f6ce..eaef42b 100644 --- a/AppImage/scripts/build_appimage.sh +++ b/AppImage/scripts/build_appimage.sh @@ -194,9 +194,20 @@ chmod +x "$APP_DIR/usr/bin/translate_cli.py" # Copy Next.js build echo "📋 Copying web dashboard..." if [ -d "$APPIMAGE_ROOT/.next" ]; then + mkdir -p "$APP_DIR/web" cp -r "$APPIMAGE_ROOT/.next" "$APP_DIR/web/" cp -r "$APPIMAGE_ROOT/public" "$APP_DIR/web/" cp "$APPIMAGE_ROOT/package.json" "$APP_DIR/web/" + + # Also try to create a static export if possible + cd "$APPIMAGE_ROOT" + if npm run export 2>/dev/null; then + echo "✅ Next.js static export created" + if [ -d "out" ]; then + cp -r "out" "$APP_DIR/web/" + fi + fi + echo "✅ Next.js build copied successfully" else echo "❌ Error: Next.js build not found even after building" diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 02a8564..122421f 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -21,419 +21,82 @@ CORS(app) # Enable CORS for Next.js frontend @app.route('/') def serve_dashboard(): - """Serve the main dashboard page""" + """Serve the main dashboard page from Next.js build""" try: - web_dir = os.path.join(os.path.dirname(__file__), '..', '.next', 'static') - index_file = os.path.join(os.path.dirname(__file__), '..', '.next', 'server', 'app', 'page.html') + next_dir = os.path.join(os.path.dirname(__file__), '..', 'web', '.next') + + # Try to serve the Next.js built index page + index_paths = [ + os.path.join(next_dir, 'server', 'app', 'page.html'), + os.path.join(next_dir, 'server', 'pages', 'index.html'), + os.path.join(os.path.dirname(__file__), '..', 'web', 'out', 'index.html'), + os.path.join(os.path.dirname(__file__), '..', 'web', 'dist', 'index.html') + ] + + for index_path in index_paths: + if os.path.exists(index_path): + return send_file(index_path) + + # If no Next.js build found, return error message + return ''' + + + ProxMenux Monitor - Build Error + +

🚨 ProxMenux Monitor - Build Error

+

Next.js application not found. The AppImage may not have been built correctly.

+

Expected paths checked:

+ +

API endpoints are still available:

+ + + + ''', 500 - if os.path.exists(index_file): - return send_file(index_file) - else: - return ''' - - - - ProxMenux Monitor - - - - - - - - - - - -
-
-
- -
-
ProxMenux Monitor
-
Proxmox System Dashboard
-
-
- 📊 -
-
proxmox-01
-
pve-node-01
-
-
-
-
-
✅ Healthy
-
Uptime: 15d 7h 23m
- - - -
-
-
- -
-
-
- - - - - -
-
- -
-
-
-
CPU Usage
- 🖥️ -
-
67.3%
-
-
-
-
↓ 2.1% from last hour
-
- -
-
-
Memory Usage
- 💾 -
-
15.8 GB
-
-
-
-
49.4% of 32 GB • ↑ 1.2 GB
-
- -
-
-
Temperature
- 🌡️ -
-
52°C
-
- Normal -
-
Max: 78°C • Avg: 48°C
-
- -
-
-
Active VMs
- 🖥️ -
-
12
-
- 8 Running - 4 Stopped -
-
Total: 16 VMs configured
-
-
- -
-
-
- 📈 - CPU Usage (24h) -
-
- CPU Usage Chart
- Real-time data from /api/system -
-
- -
-
- 💾 - Memory Usage (24h) -
-
- Memory Usage Chart
- Real-time data from /api/system -
-
-
- -
-
-
- 🖥️ - System Information -
-
- Hostname: - proxmox-01 -
-
- Version: - PVE 8.1.3 -
-
- Kernel: - 6.5.11-7-pve -
-
- Architecture: - x86_64 -
-
- -
-
- 👥 - Active Sessions -
-
- Web Console: - 3 active -
-
- SSH Sessions: - 1 active -
-
- API Calls: - 247/hour -
-
- -
-
- - Power & Performance -
-
- Power State: - Running -
-
- Load Average: - 1.23, 1.45, 1.67 -
-
- Boot Time: - 2.3s -
-
-
- - -
- - - - - ''' except Exception as e: print(f"Error serving dashboard: {e}") - return jsonify({'error': 'Dashboard not available'}), 500 + return jsonify({'error': f'Dashboard not available: {str(e)}'}), 500 @app.route('/manifest.json') def serve_manifest(): """Serve PWA manifest""" - return send_from_directory(os.path.join(os.path.dirname(__file__), '..', 'public'), 'manifest.json') + try: + manifest_paths = [ + os.path.join(os.path.dirname(__file__), '..', 'web', 'public', 'manifest.json'), + os.path.join(os.path.dirname(__file__), '..', 'public', 'manifest.json') + ] + + for manifest_path in manifest_paths: + if os.path.exists(manifest_path): + return send_file(manifest_path) + + # Return default manifest if not found + return jsonify({ + "name": "ProxMenux Monitor", + "short_name": "ProxMenux", + "description": "Proxmox System Monitoring Dashboard", + "start_url": "/", + "display": "standalone", + "background_color": "#0a0a0a", + "theme_color": "#4f46e5", + "icons": [ + { + "src": "/images/proxmenux-logo.png", + "sizes": "256x256", + "type": "image/png" + } + ] + }) + except Exception as e: + print(f"Error serving manifest: {e}") + return jsonify({}), 404 @app.route('/sw.js') def serve_sw(): @@ -463,19 +126,34 @@ def serve_sw(): }); ''', 200, {'Content-Type': 'application/javascript'} +@app.route('/_next/') +def serve_next_static(filename): + """Serve Next.js static files""" + try: + next_static_dir = os.path.join(os.path.dirname(__file__), '..', 'web', '.next', 'static') + if os.path.exists(os.path.join(next_static_dir, filename)): + return send_from_directory(next_static_dir, filename) + return '', 404 + except Exception as e: + print(f"Error serving Next.js static file {filename}: {e}") + return '', 404 + @app.route('/') def serve_static_files(filename): """Serve static files (icons, etc.)""" try: - # Try public directory first - public_dir = os.path.join(os.path.dirname(__file__), '..', 'public') - if os.path.exists(os.path.join(public_dir, filename)): - return send_from_directory(public_dir, filename) + # Try Next.js public directory first + public_paths = [ + os.path.join(os.path.dirname(__file__), '..', 'web', 'public'), + os.path.join(os.path.dirname(__file__), '..', 'public'), + os.path.join(os.path.dirname(__file__), '..', 'web', 'out'), + os.path.join(os.path.dirname(__file__), '..', 'web', '.next', 'static') + ] - # Try Next.js static directory - static_dir = os.path.join(os.path.dirname(__file__), '..', '.next', 'static') - if os.path.exists(os.path.join(static_dir, filename)): - return send_from_directory(static_dir, filename) + for public_dir in public_paths: + file_path = os.path.join(public_dir, filename) + if os.path.exists(file_path): + return send_from_directory(public_dir, filename) return '', 404 except Exception as e: @@ -486,12 +164,18 @@ def serve_static_files(filename): def serve_images(filename): """Serve image files""" try: - web_dir = os.path.join(os.path.dirname(__file__), '..', 'web', 'public', 'images') - if os.path.exists(os.path.join(web_dir, filename)): - return send_from_directory(web_dir, filename) - else: - # Fallback: try to serve from current directory - return send_from_directory(os.path.dirname(__file__), filename) + image_paths = [ + os.path.join(os.path.dirname(__file__), '..', 'web', 'public', 'images'), + os.path.join(os.path.dirname(__file__), '..', 'public', 'images'), + os.path.dirname(__file__) + ] + + for image_dir in image_paths: + file_path = os.path.join(image_dir, filename) + if os.path.exists(file_path): + return send_from_directory(image_dir, filename) + + return '', 404 except Exception as e: print(f"Error serving image {filename}: {e}") return '', 404