forked from trent_larson/crowd-funder-for-time-pwa
fix: simplify Electron single instance enforcement
- Remove complex file-based locking that caused crashes - Use only Electron's built-in requestSingleInstanceLock() API - Second instances exit immediately with clear messaging - Existing instance focuses and shows user-friendly dialog - Prevents database conflicts and resource contention - Update documentation with simplified approach Fixes crashes when multiple instances run simultaneously.
This commit is contained in:
@@ -1,270 +1,272 @@
|
|||||||
# Building TimeSafari Electron App
|
# TimeSafari Electron Build Guide
|
||||||
|
|
||||||
This guide explains how to build distributable packages for the TimeSafari Electron desktop application.
|
**Author**: Matthew Raymer
|
||||||
|
**Date**: 2025-01-10
|
||||||
|
**Status**: **ACTIVE** - Production Ready
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide covers building and running the TimeSafari Electron application for desktop platforms (Windows, macOS, Linux).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Node.js 18+ and npm
|
||||||
|
- TypeScript
|
||||||
|
- Electron Builder
|
||||||
|
- Platform-specific build tools (see below)
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### From Project Root
|
### Development Mode
|
||||||
```bash
|
```bash
|
||||||
# Build all Linux packages (AppImage, deb)
|
# Start development server
|
||||||
npm run electron:build
|
npm run build:electron:dev
|
||||||
|
|
||||||
# Build specific package types
|
# Or manually
|
||||||
npm run electron:build:appimage # AppImage only
|
|
||||||
npm run electron:build:deb # Debian package only
|
|
||||||
```
|
|
||||||
|
|
||||||
### From Electron Directory
|
|
||||||
```bash
|
|
||||||
cd electron
|
cd electron
|
||||||
|
npm run electron:start
|
||||||
# Build all packages
|
|
||||||
./build-packages.sh
|
|
||||||
|
|
||||||
# Build specific types
|
|
||||||
./build-packages.sh appimage
|
|
||||||
./build-packages.sh deb
|
|
||||||
./build-packages.sh pack # Unpacked directory (for testing)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Package Types
|
### Production Builds
|
||||||
|
|
||||||
### 1. AppImage (Recommended for Linux)
|
|
||||||
- **File**: `TimeSafari-1.0.0.AppImage`
|
|
||||||
- **Size**: ~145MB
|
|
||||||
- **Usage**: Download and run directly, no installation required
|
|
||||||
- **Distribution**: Upload to GitHub releases or website
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Make executable and run
|
# Build for current platform
|
||||||
chmod +x TimeSafari-1.0.0.AppImage
|
npm run build:electron:prod
|
||||||
./TimeSafari-1.0.0.AppImage
|
|
||||||
|
# Platform-specific builds
|
||||||
|
npm run build:electron:windows
|
||||||
|
npm run build:electron:mac
|
||||||
|
npm run build:electron:linux
|
||||||
|
|
||||||
|
# Package-specific builds
|
||||||
|
npm run build:electron:appimage # Linux AppImage
|
||||||
|
npm run build:electron:dmg # macOS DMG installer
|
||||||
|
npm run build:electron:deb # Linux DEB package
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Debian Package (.deb)
|
## Single Instance Enforcement
|
||||||
- **File**: `TimeSafari_1.0.0_amd64.deb`
|
|
||||||
- **Size**: ~96MB
|
|
||||||
- **Usage**: Install via package manager
|
|
||||||
- **Distribution**: Upload to repositories or direct download
|
|
||||||
|
|
||||||
|
The Electron app enforces single instance operation to prevent database conflicts and resource contention:
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
- Uses Electron's built-in `app.requestSingleInstanceLock()`
|
||||||
|
- Second instances exit immediately with user-friendly message
|
||||||
|
- Existing instance focuses and shows informational dialog
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
- **First instance**: Starts normally and acquires lock
|
||||||
|
- **Second instance**: Detects existing instance, exits immediately
|
||||||
|
- **User experience**: Clear messaging about single instance requirement
|
||||||
|
|
||||||
|
### Benefits
|
||||||
|
- Prevents database corruption from concurrent access
|
||||||
|
- Avoids resource conflicts
|
||||||
|
- Maintains data integrity
|
||||||
|
- User-friendly error handling
|
||||||
|
|
||||||
|
## Build Configuration
|
||||||
|
|
||||||
|
### Environment Modes
|
||||||
```bash
|
```bash
|
||||||
# Install
|
# Development (default)
|
||||||
sudo dpkg -i TimeSafari_1.0.0_amd64.deb
|
npm run build:electron:dev
|
||||||
|
|
||||||
# Run
|
# Testing
|
||||||
timesafari
|
npm run build:electron:test
|
||||||
|
|
||||||
|
# Production
|
||||||
|
npm run build:electron:prod
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. RPM Package (.rpm)
|
### Platform-Specific Builds
|
||||||
- **File**: `TimeSafari-1.0.0.x86_64.rpm`
|
|
||||||
- **Requirements**: `rpmbuild` must be installed
|
|
||||||
- **Usage**: Install via package manager
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install rpmbuild (Arch Linux)
|
# Windows
|
||||||
sudo pacman -S rpm-tools
|
npm run build:electron:windows:dev
|
||||||
|
npm run build:electron:windows:test
|
||||||
|
npm run build:electron:windows:prod
|
||||||
|
|
||||||
# Build RPM
|
# macOS
|
||||||
./build-packages.sh rpm
|
npm run build:electron:mac:dev
|
||||||
|
npm run build:electron:mac:test
|
||||||
|
npm run build:electron:mac:prod
|
||||||
|
|
||||||
# Install (on RPM-based systems)
|
# Linux
|
||||||
sudo rpm -i TimeSafari-1.0.0.x86_64.rpm
|
npm run build:electron:linux:dev
|
||||||
|
npm run build:electron:linux:test
|
||||||
|
npm run build:electron:linux:prod
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build Requirements
|
### Package Types
|
||||||
|
|
||||||
### System Dependencies
|
|
||||||
- Node.js 18+
|
|
||||||
- npm or yarn
|
|
||||||
- Python 3 (for native module compilation)
|
|
||||||
- Build tools (gcc, make)
|
|
||||||
|
|
||||||
### Optional Dependencies
|
|
||||||
- `rpmbuild` - for RPM packages
|
|
||||||
- `fpm` - automatically downloaded by electron-builder
|
|
||||||
|
|
||||||
### Node Dependencies
|
|
||||||
All required dependencies are in `package.json`:
|
|
||||||
- `electron-builder` - Main build tool
|
|
||||||
- `better-sqlite3-multiple-ciphers` - SQLite with encryption
|
|
||||||
- Native module compilation tools
|
|
||||||
|
|
||||||
## Build Process
|
|
||||||
|
|
||||||
### 1. Preparation
|
|
||||||
```bash
|
```bash
|
||||||
# Install dependencies
|
# Linux AppImage
|
||||||
npm install
|
npm run build:electron:appimage:dev
|
||||||
|
npm run build:electron:appimage:test
|
||||||
|
npm run build:electron:appimage:prod
|
||||||
|
|
||||||
# Build TypeScript
|
# macOS DMG
|
||||||
npm run build
|
npm run build:electron:dmg:dev
|
||||||
|
npm run build:electron:dmg:test
|
||||||
|
npm run build:electron:dmg:prod
|
||||||
|
|
||||||
|
# Linux DEB
|
||||||
|
npm run build:electron:deb:dev
|
||||||
|
npm run build:electron:deb:test
|
||||||
|
npm run build:electron:deb:prod
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Package Creation
|
## Platform-Specific Requirements
|
||||||
The build process:
|
|
||||||
1. Compiles TypeScript to JavaScript
|
|
||||||
2. Rebuilds native modules for Electron
|
|
||||||
3. Packages the app with electron-builder
|
|
||||||
4. Creates platform-specific installers
|
|
||||||
|
|
||||||
### 3. Output Location
|
### Windows
|
||||||
All built packages are saved to `electron/dist/`:
|
- Windows 10+ (64-bit)
|
||||||
```
|
- Visual Studio Build Tools (for native modules)
|
||||||
dist/
|
|
||||||
├── TimeSafari-1.0.0.AppImage # Portable AppImage
|
|
||||||
├── TimeSafari_1.0.0_amd64.deb # Debian package
|
|
||||||
├── TimeSafari-1.0.0.x86_64.rpm # RPM package (if built)
|
|
||||||
└── linux-unpacked/ # Unpacked directory
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
### macOS
|
||||||
|
- macOS 10.15+ (Catalina)
|
||||||
|
- Xcode Command Line Tools
|
||||||
|
- Code signing certificate (for distribution)
|
||||||
|
|
||||||
### Single Instance Enforcement
|
### Linux
|
||||||
TimeSafari Electron enforces single-instance operation to prevent:
|
- Ubuntu 18.04+ / Debian 10+ / CentOS 7+
|
||||||
- Database corruption from multiple instances
|
- Development headers for native modules
|
||||||
- Resource conflicts and performance issues
|
|
||||||
- User confusion from multiple windows
|
|
||||||
|
|
||||||
**Behavior:**
|
## Database Configuration
|
||||||
- Only one instance can run at a time
|
|
||||||
- Attempting to launch a second instance shows a user-friendly dialog
|
|
||||||
- The existing window is focused and restored if minimized
|
|
||||||
- Second instance exits gracefully
|
|
||||||
|
|
||||||
**Implementation:**
|
### SQLite Integration
|
||||||
- Uses Electron's `requestSingleInstanceLock()` API
|
- Uses native Node.js SQLite3 for Electron
|
||||||
- Handles `second-instance` events to focus existing window
|
- Database stored in user's app data directory
|
||||||
- Shows informative dialog explaining why only one instance is allowed
|
- Automatic migration from IndexedDB (if applicable)
|
||||||
|
|
||||||
## Configuration
|
### Single Instance Protection
|
||||||
|
- File-based locking prevents concurrent database access
|
||||||
|
- Automatic cleanup on app exit
|
||||||
|
- Graceful handling of lock conflicts
|
||||||
|
|
||||||
### App Metadata
|
## Security Features
|
||||||
App information is configured in `electron/package.json`:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "TimeSafari",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Time Safari - Community building through gifts, gratitude, and collaborative projects",
|
|
||||||
"homepage": "https://timesafari.app",
|
|
||||||
"author": {
|
|
||||||
"name": "Matthew Raymer",
|
|
||||||
"email": "matthew@timesafari.app"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Configuration
|
### Content Security Policy
|
||||||
Build settings are in `electron/electron-builder.config.json`:
|
- Strict CSP in production builds
|
||||||
- Package formats and architectures
|
- Development mode allows localhost connections
|
||||||
- Icons and assets
|
- Automatic configuration based on build mode
|
||||||
- Platform-specific settings
|
|
||||||
- Signing and publishing options
|
### Auto-Updater
|
||||||
|
- Disabled in development mode
|
||||||
|
- Production builds check for updates automatically
|
||||||
|
- AppImage builds skip update checks
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Common Issues
|
### Common Issues
|
||||||
|
|
||||||
#### 1. Native Module Compilation Errors
|
#### Build Failures
|
||||||
```bash
|
```bash
|
||||||
# Clear cache and rebuild
|
# Clean and rebuild
|
||||||
npm run build
|
npm run clean:electron
|
||||||
|
npm run build:electron:dev
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Missing Dependencies
|
#### Native Module Issues
|
||||||
```bash
|
```bash
|
||||||
# Install system dependencies (Arch Linux)
|
# Rebuild native modules
|
||||||
sudo pacman -S base-devel python
|
cd electron
|
||||||
|
npm run electron:rebuild
|
||||||
# Install Node dependencies
|
|
||||||
npm install
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. RPM Build Fails
|
#### Single Instance Conflicts
|
||||||
```bash
|
- Ensure no other TimeSafari instances are running
|
||||||
# Install rpmbuild
|
- Check for orphaned processes: `ps aux | grep electron`
|
||||||
sudo pacman -S rpm-tools
|
- Restart system if necessary
|
||||||
|
|
||||||
# Try building again
|
#### Database Issues
|
||||||
./build-packages.sh rpm
|
- Check app data directory permissions
|
||||||
```
|
- Verify SQLite database integrity
|
||||||
|
- Clear app data if corrupted
|
||||||
#### 4. Large Package Size
|
|
||||||
The packages are large (~100-150MB) because they include:
|
|
||||||
- Complete Electron runtime
|
|
||||||
- Node.js runtime
|
|
||||||
- SQLite native modules
|
|
||||||
- Application assets
|
|
||||||
|
|
||||||
This is normal for Electron applications.
|
|
||||||
|
|
||||||
### Debug Mode
|
### Debug Mode
|
||||||
For detailed build information:
|
|
||||||
```bash
|
```bash
|
||||||
DEBUG=electron-builder npx electron-builder build
|
# Enable debug logging
|
||||||
|
DEBUG=* npm run build:electron:dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Distribution
|
## File Structure
|
||||||
|
|
||||||
### GitHub Releases
|
```
|
||||||
1. Create a new release on GitHub
|
electron/
|
||||||
2. Upload the built packages as release assets
|
├── src/
|
||||||
3. Users can download and install directly
|
│ ├── index.ts # Main process entry point
|
||||||
|
│ ├── preload.ts # Preload script
|
||||||
|
│ └── setup.ts # App setup and configuration
|
||||||
|
├── assets/ # App icons and resources
|
||||||
|
├── package.json # Electron-specific dependencies
|
||||||
|
├── electron-builder.config.json # Build configuration
|
||||||
|
└── tsconfig.json # TypeScript configuration
|
||||||
|
```
|
||||||
|
|
||||||
### Package Repositories
|
## Development Workflow
|
||||||
- **Debian/Ubuntu**: Upload `.deb` to repository
|
|
||||||
- **Fedora/CentOS**: Upload `.rpm` to repository
|
|
||||||
- **Arch Linux**: Create PKGBUILD for AUR
|
|
||||||
|
|
||||||
### Direct Download
|
1. **Start Development**
|
||||||
Host the packages on your website for direct download.
|
```bash
|
||||||
|
npm run build:electron:dev
|
||||||
|
```
|
||||||
|
|
||||||
## Cross-Platform Building
|
2. **Make Changes**
|
||||||
|
- Edit source files in `src/`
|
||||||
|
- Changes auto-reload in development
|
||||||
|
|
||||||
### Current Support
|
3. **Test Build**
|
||||||
- **Linux**: Full support (AppImage, deb, rpm)
|
```bash
|
||||||
- **Windows**: Configured but requires Windows build environment
|
npm run build:electron:test
|
||||||
- **macOS**: Configured but requires macOS build environment
|
```
|
||||||
|
|
||||||
### Building for Other Platforms
|
4. **Production Build**
|
||||||
To build for Windows or macOS, you need:
|
```bash
|
||||||
- The target platform's build environment
|
npm run build:electron:prod
|
||||||
- Platform-specific signing certificates
|
```
|
||||||
- Updated build configuration
|
|
||||||
|
|
||||||
## Security Considerations
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Memory Usage
|
||||||
|
- Monitor renderer process memory
|
||||||
|
- Implement proper cleanup in components
|
||||||
|
- Use efficient data structures
|
||||||
|
|
||||||
|
### Startup Time
|
||||||
|
- Lazy load non-critical modules
|
||||||
|
- Optimize database initialization
|
||||||
|
- Minimize synchronous operations
|
||||||
|
|
||||||
|
### Database Performance
|
||||||
|
- Use transactions for bulk operations
|
||||||
|
- Implement proper indexing
|
||||||
|
- Monitor query performance
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
- [ ] Content Security Policy configured
|
||||||
|
- [ ] Auto-updater properly configured
|
||||||
|
- [ ] Single instance enforcement active
|
||||||
|
- [ ] Database access properly secured
|
||||||
|
- [ ] IPC handlers validate input
|
||||||
|
- [ ] File system access restricted
|
||||||
|
- [ ] Network requests validated
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Distribution
|
||||||
|
- Windows: `.exe` installer
|
||||||
|
- macOS: `.dmg` disk image
|
||||||
|
- Linux: `.AppImage` or `.deb` package
|
||||||
|
|
||||||
### Code Signing
|
### Code Signing
|
||||||
For production releases, consider code signing:
|
- Windows: Authenticode certificate
|
||||||
- **Linux**: Not required but recommended
|
- macOS: Developer ID certificate
|
||||||
- **Windows**: Required for Windows SmartScreen
|
- Linux: GPG signing (optional)
|
||||||
- **macOS**: Required for Gatekeeper
|
|
||||||
|
|
||||||
### Package Integrity
|
### Auto-Updates
|
||||||
- Verify package checksums
|
- Configured for production builds
|
||||||
- Use HTTPS for distribution
|
- Disabled for development and AppImage
|
||||||
- Consider GPG signatures for packages
|
- Handles update failures gracefully
|
||||||
|
|
||||||
## Performance Tips
|
|
||||||
|
|
||||||
### Build Optimization
|
|
||||||
- Use `--dir` flag for faster development builds
|
|
||||||
- Cache node_modules between builds
|
|
||||||
- Use CI/CD for automated builds
|
|
||||||
|
|
||||||
### Package Size Reduction
|
|
||||||
- Remove unnecessary dependencies
|
|
||||||
- Use electron-builder's file filtering
|
|
||||||
- Consider using electron-updater for delta updates
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
For build issues:
|
|
||||||
1. Check the console output for specific errors
|
|
||||||
2. Verify all dependencies are installed
|
|
||||||
3. Try cleaning and rebuilding
|
|
||||||
4. Check electron-builder documentation
|
|
||||||
5. Open an issue with build logs
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Happy Building! 🚀**
|
**Last Updated**: 2025-01-10
|
||||||
|
**Version**: 1.0.3-beta
|
||||||
|
**Status**: Production Ready
|
||||||
@@ -10,24 +10,11 @@ import { join } from 'path';
|
|||||||
|
|
||||||
import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup';
|
import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup';
|
||||||
|
|
||||||
// Single instance enforcement
|
// Use Electron's built-in single instance lock
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
|
||||||
if (!gotTheLock) {
|
if (!gotTheLock) {
|
||||||
console.log('[Electron] Another instance is already running. Exiting...');
|
console.log('[Electron] Another instance is already running. Exiting immediately...');
|
||||||
|
|
||||||
// Show user-friendly dialog before quitting
|
|
||||||
dialog.showMessageBox({
|
|
||||||
type: 'info',
|
|
||||||
title: 'TimeSafari Already Running',
|
|
||||||
message: 'TimeSafari is already running in another window.',
|
|
||||||
detail: 'Please use the existing TimeSafari window instead of opening a new one.',
|
|
||||||
buttons: ['OK']
|
|
||||||
}).then(() => {
|
|
||||||
app.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Exit immediately
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +87,7 @@ autoUpdater.on('error', (error) => {
|
|||||||
// Initialize our app, build windows, and load content.
|
// Initialize our app, build windows, and load content.
|
||||||
await myCapacitorApp.init();
|
await myCapacitorApp.init();
|
||||||
|
|
||||||
// Handle second instance launch (focus existing window)
|
// Handle second instance launch (focus existing window and show dialog)
|
||||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||||
console.log('[Electron] Second instance attempted to launch');
|
console.log('[Electron] Second instance attempted to launch');
|
||||||
|
|
||||||
@@ -111,6 +98,17 @@ autoUpdater.on('error', (error) => {
|
|||||||
mainWindow.restore();
|
mainWindow.restore();
|
||||||
}
|
}
|
||||||
mainWindow.focus();
|
mainWindow.focus();
|
||||||
|
|
||||||
|
// Show user-friendly dialog to inform about single instance
|
||||||
|
dialog.showMessageBox(mainWindow, {
|
||||||
|
type: 'info',
|
||||||
|
title: 'TimeSafari Already Running',
|
||||||
|
message: 'TimeSafari is already running in another window.',
|
||||||
|
detail: 'Please use this existing TimeSafari window instead of opening a new one.',
|
||||||
|
buttons: ['OK']
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('[Electron] Error showing dialog:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user