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:
Matthew Raymer
2025-07-11 07:46:08 +00:00
parent 12496589a3
commit 09a791622c
2 changed files with 222 additions and 222 deletions

View File

@@ -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

View File

@@ -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);
});
} }
}); });