Skip to content

Commit 9080283

Browse files
author
valibali
committed
added firewall exception handling for rigctld
1 parent 71f0f7f commit 9080283

File tree

8 files changed

+620
-65
lines changed

8 files changed

+620
-65
lines changed

CLAUDE.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
HamLedger is a modern amateur radio logging application built as an Electron desktop app with Vue 3 frontend. It provides QSO (radio contact) logging, real-time rig control via Hamlib, DX cluster integration, WSJT-X integration, and various ham radio utilities.
8+
9+
## Development Commands
10+
11+
### Running the Application
12+
```bash
13+
npm run app:dev # Start development server with hot reload
14+
npm run app:build # Build for production
15+
npm run app:preview # Preview production build
16+
```
17+
18+
### Code Quality
19+
```bash
20+
npm run lint # Run ESLint
21+
npm run format # Format code with Prettier
22+
npm run format:check # Check code formatting
23+
```
24+
25+
### Testing
26+
```bash
27+
npm test # Run tests with Vitest
28+
npm run test:coverage # Run tests with coverage report
29+
```
30+
31+
### Building Distributables
32+
```bash
33+
npm run build:win # Build Windows installer
34+
npm run build:mac # Build macOS installer
35+
npm run build:linux # Build Linux packages (AppImage, deb, rpm)
36+
npm run build:all # Build for all platforms
37+
```
38+
39+
### TypeScript
40+
```bash
41+
npm run ts # Compile TypeScript once
42+
npm run watch # Watch TypeScript files for changes
43+
```
44+
45+
## Architecture Overview
46+
47+
### Electron Architecture
48+
49+
This is a standard Electron app with clear separation between main and renderer processes:
50+
51+
- **Main Process** (`src/electron/main/main.ts`): Handles native operations, IPC handlers, file system access, database operations, and manages background processes (rigctld, WSJT-X UDP server)
52+
- **Preload Script** (`src/electron/preload/preload.ts`): Exposes safe IPC channels to renderer via `window.electronAPI`
53+
- **Renderer Process** (`src/`): Vue 3 application with Pinia state management
54+
55+
### Communication Flow
56+
57+
1. Vue components → Pinia stores → `window.electronAPI` → IPC → Main process
58+
2. Main process background services emit events → IPC → Preload → Store listeners → Vue components
59+
60+
### State Management (Pinia Stores)
61+
62+
Located in `src/store/`:
63+
64+
- **qso.ts**: Core QSO logging, station lookup, form state, WSJT-X decode handling
65+
- **rig.ts**: Rig control state (frequency, mode, S-meter, split operation)
66+
- **dxCluster.ts**: DX cluster spot management
67+
- **propagation.ts**: Solar/propagation data
68+
- **weather.ts**: Weather information
69+
- **index.ts**: Basic counter store (minimal usage)
70+
71+
Stores are initialized in `App.vue` after setup wizard completes.
72+
73+
### Database Layer
74+
75+
**PouchDB** is used for local QSO storage (`src/services/DatabaseService.ts`):
76+
- Database location: `userData/HamLedger.db` (managed by Electron's app.getPath)
77+
- All QSO CRUD operations go through `DatabaseService`
78+
- Main process handles all database operations; renderer communicates via IPC
79+
80+
### External Service Integration
81+
82+
**Services** (`src/services/`):
83+
- **RigctldService.ts**: TCP connection to rigctld daemon for rig control (Hamlib)
84+
- **WSJTXService.ts**: UDP server (port 2237) that receives WSJT-X binary protocol messages
85+
- **QRZService.ts**: Fetches station data from QRZ.com XML API
86+
- **OnlineStationService.ts**: Additional station lookup services
87+
- **DatabaseService.ts**: PouchDB wrapper for QSO persistence
88+
89+
### Background Process Management
90+
91+
Main process manages two key background services:
92+
93+
1. **rigctld** (Hamlib): Started automatically on app launch, uses rig configuration from settings
94+
2. **WSJT-X UDP Server**: Listens for decode messages and logged QSOs, enabled via settings
95+
96+
### Component Structure
97+
98+
- **App.vue**: Root component, handles initialization and setup wizard
99+
- **MainContent.vue**: Main layout container
100+
- **SideBar.vue**: Navigation sidebar
101+
- **QsoPanel.vue**: Main QSO logging panel
102+
- **LogBook.vue**: QSO history view
103+
- **DxCluster.vue**: DX spot display
104+
- **AppHeader.vue**: Contains sub-components:
105+
- **FreqSMeter.vue**: Frequency and S-meter display
106+
- **RigControl.vue**: Rig control interface
107+
- **PropClockWeather.vue**: Propagation, UTC clock, and weather
108+
- **qso/** subdirectory:
109+
- **QsoInput.vue**: Main QSO entry form
110+
- **QsoDetailDialog.vue**: View QSO details
111+
- **QsoEditDialog.vue**: Edit existing QSO
112+
- **RemoteStation.vue**: Remote station information display
113+
114+
### Utility Modules
115+
116+
Located in `src/utils/`:
117+
118+
- **adif.ts**: ADIF file format parsing and generation
119+
- **callsign.ts**: Callsign parsing and validation
120+
- **maidenhead.ts**: Maidenhead grid square calculations
121+
- **distance.ts**: Distance calculations between stations
122+
- **bands.ts**: Amateur radio band definitions and frequency conversions
123+
- **geocoding.ts**: Location geocoding utilities
124+
- **dateHelper.ts**: UTC time formatting
125+
- **smeterHelper.ts**: S-meter value conversions
126+
- **configHelper.ts**: Configuration file management (settings.json)
127+
128+
### Configuration Management
129+
130+
Settings are stored in JSON format:
131+
- **Default settings**: `src/settings.json` (bundled with app)
132+
- **User settings**: `userData/settings.json` (platform-specific location)
133+
- **Schema validation**: `src/settings.schema.json`
134+
135+
The `configHelper` utility manages loading, merging, and saving settings. Setup wizard creates initial user settings on first run.
136+
137+
### Type Definitions
138+
139+
Located in `src/types/`:
140+
- **qso.ts**: QSO entry structure
141+
- **rig.ts**: Rig state and rigctld types
142+
- **wsjtx.ts**: WSJT-X protocol message types
143+
- **station.ts**: Station data structures
144+
- **config.ts**: Configuration types
145+
- **electron.ts**: Window API type declarations for TypeScript
146+
147+
## Key Technical Details
148+
149+
### TypeScript Configuration
150+
- Two separate tsconfig files:
151+
- Root `tsconfig.json`: Electron main/preload compilation (outputs to `dist/electron/`)
152+
- `tsconfig.node.json`: Node.js specific types
153+
- Vite handles Vue/TypeScript compilation for renderer process
154+
155+
### Build Process
156+
1. Vue app built with Vite → `dist/` directory
157+
2. TypeScript (Electron) compiled with tsc → `dist/electron/`
158+
3. electron-builder packages everything into platform-specific installers
159+
160+
### WSJT-X Integration
161+
- WSJT-X broadcasts UDP datagrams on port 2237 (configurable)
162+
- Binary protocol parsing in `WSJTXService.ts`
163+
- Decodes are displayed in QSO panel for quick logging
164+
- Logged QSOs can be automatically added to HamLedger
165+
166+
### Rigctld Integration
167+
- Hamlib's rigctld provides TCP interface to radio transceivers
168+
- HamLedger can auto-start rigctld or connect to existing instance
169+
- Polling-based updates for frequency, mode, S-meter
170+
- Supports split operation, VFO selection, PTT control
171+
172+
### ADIF Support
173+
- Import: Opens file dialog, parses ADIF, bulk imports to database
174+
- Export: Generates ADIF from database QSOs
175+
- Parser handles standard ADIF tags and field formats
176+
177+
## Testing Notes
178+
179+
- Tests use Vitest with jsdom environment
180+
- Component tests use Vue Test Utils with Pinia
181+
- Located in `src/components/__tests__/`
182+
- Run with `npm test` or `npm run test:coverage`
183+
184+
## Important Development Considerations
185+
186+
- The app requires native modules (PouchDB, Hamlib integration), so avoid introducing additional native dependencies without careful consideration
187+
- Proxy settings are supported for network requests (QRZ, propagation data)
188+
- The app is GPL v3 licensed with commercial clause
189+
- Settings file changes require app restart
190+
- Database schema is flexible (document-based), but QSO structure should maintain ADIF compatibility

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "hamledger",
33
"private": true,
4-
"version": "1.3.0",
4+
"version": "1.3.1",
55
"author": {
66
"name": "Balazs Valkony - HA5XB",
77
"email": "[email protected]"

src/components/header/RigControl.vue

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default {
2727
showConnectionDialog: false,
2828
showWSJTXDialog: false,
2929
showTakeBackDialog: false,
30+
showAdminRetryDialog: false,
3031
rigModels: [] as RigModel[],
3132
loadingModels: false,
3233
wsjtxEnabled: false,
@@ -170,7 +171,7 @@ export default {
170171
171172
async handleConnect() {
172173
try {
173-
await this.rigStore.connect(
174+
const response = await this.rigStore.connect(
174175
this.connectionForm.host,
175176
this.connectionForm.port,
176177
this.connectionForm.model,
@@ -181,12 +182,64 @@ export default {
181182
this.showConnectionDialog = false;
182183
// Start polling for rig state updates
183184
this.rigStore.startPolling(2000); // Poll every 2 seconds
185+
} else if (response.shouldRetry) {
186+
// Firewall was configured, automatically retry connection
187+
console.log('Firewall configured, retrying connection...');
188+
await new Promise(resolve => setTimeout(resolve, 1000));
189+
190+
// Retry connection without attempting firewall fix again
191+
const retryResponse = await this.rigStore.connect(
192+
this.connectionForm.host,
193+
this.connectionForm.port,
194+
this.connectionForm.model,
195+
this.connectionForm.device
196+
);
197+
198+
if (this.rigStore.isConnected) {
199+
this.showConnectionDialog = false;
200+
this.rigStore.startPolling(2000);
201+
} else {
202+
// Still failed after firewall fix, offer admin option
203+
console.log('Connection still failed after firewall fix');
204+
this.showAdminRetryDialog = true;
205+
}
206+
} else if (response.userCancelled) {
207+
// User cancelled UAC prompt for firewall
208+
console.log('User cancelled firewall configuration');
209+
// Don't show admin dialog, user explicitly cancelled
210+
} else if (response.firewallConfigured === false && response.firewallError) {
211+
// Firewall configuration failed for some reason
212+
console.error('Firewall configuration failed:', response.firewallError);
213+
// Offer admin option
214+
this.showAdminRetryDialog = true;
184215
}
185216
} catch (error) {
186217
console.error('Connection failed:', error);
187218
}
188219
},
189220
221+
async handleRunAsAdmin() {
222+
this.showAdminRetryDialog = false;
223+
try {
224+
const response = await this.rigStore.startRigctldElevated();
225+
226+
if (this.rigStore.isConnected) {
227+
this.showConnectionDialog = false;
228+
this.rigStore.startPolling(2000);
229+
} else if (response.userCancelled) {
230+
console.log('User cancelled elevated rigctld start');
231+
} else {
232+
console.error('Failed to start elevated rigctld:', response.error);
233+
}
234+
} catch (error) {
235+
console.error('Error starting elevated rigctld:', error);
236+
}
237+
},
238+
239+
closeAdminRetryDialog() {
240+
this.showAdminRetryDialog = false;
241+
},
242+
190243
async handleReconnect() {
191244
await this.rigStore.handleReconnect();
192245
if (this.rigStore.isConnected) {
@@ -558,6 +611,31 @@ export default {
558611
</div>
559612
</div>
560613
</div>
614+
615+
<!-- Run as Administrator Dialog -->
616+
<div v-if="showAdminRetryDialog" class="connection-dialog-overlay" @click="closeAdminRetryDialog">
617+
<div class="connection-dialog" @click.stop>
618+
<h3>🔒 Connection Failed</h3>
619+
<div class="warning-content">
620+
<p>
621+
Unable to connect to rigctld even after configuring the firewall.
622+
</p>
623+
<p>
624+
Would you like to try running rigctld with administrator privileges?
625+
</p>
626+
<p class="admin-note">
627+
<strong>Note:</strong> This is a one-time action. On the next app launch, rigctld will
628+
start with normal privileges again.
629+
</p>
630+
</div>
631+
<div class="dialog-buttons">
632+
<button type="button" @click="closeAdminRetryDialog">Cancel</button>
633+
<button type="button" class="connect-btn" @click="handleRunAsAdmin">
634+
Run as Administrator
635+
</button>
636+
</div>
637+
</div>
638+
</div>
561639
</div>
562640
</template>
563641

0 commit comments

Comments
 (0)