fix: Properly parse compound handshake time and fix q key handling

- Fix parseHandshake to correctly parse number-unit pairs from '14 hours, 24 minutes, 40 seconds ago'
  Previous logic tried to parse unit words instead of finding associated numbers
  Now correctly accumulates all time units (hours, minutes, seconds)
- Fix q key handling to properly check screen type before quitting
  Only quit application when 'q' is pressed on list screen
  Other screens (detail, add, help) now handle 'q' to navigate back

Note: q key navigation may still need investigation for edge cases
This commit is contained in:
Calmcacil
2026-01-12 19:27:21 +01:00
parent e0f8210c17
commit 5d129562e2
3 changed files with 35 additions and 22 deletions

View File

@@ -29,11 +29,12 @@
{"id":"wg-admin-q0x","title":"Research and document Dracula/Everforest color schemes","description":"Research and document the official color specifications for Dracula and Everforest color schemes. Extract hex values for primary, success, warning, error, and muted colors to use in the TUI theme implementation.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:07:40.305301935+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:08:01.204915996+01:00","closed_at":"2026-01-12T19:08:01.204915996+01:00","close_reason":"Research completed - documented Dracula and Everforest color specifications with hex values for implementation"}
{"id":"wg-admin-qpy","title":"Refactor installation into wg-install.sh","description":"Extract install logic from wireguard.sh into dedicated wg-install.sh script. Handle: dependency checks, package installation, firewall setup (nftables), server key generation, interface initialization, systemd service setup. Use interactive 'read' prompts for settings with 'WGI_' prefixed environment variable overrides.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.151817177+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:50:42.168393277+01:00","closed_at":"2026-01-12T16:50:42.168393277+01:00","close_reason":"Created wg-install.sh script with complete installation logic extracted from wireguard.sh. Script includes dependency checks, package installation, nftables firewall setup, server key generation, interface initialization, and systemd service setup. Uses interactive prompts with WGI_ prefixed environment variable overrides. All validation and error handling maintained with atomic operations and proper cleanup. Test suite (test-wg-install.sh) created with 35 tests all passing.","dependencies":[{"issue_id":"wg-admin-qpy","depends_on_id":"wg-admin-37o","type":"blocks","created_at":"2026-01-12T16:28:20.30398105+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-qpy","depends_on_id":"wg-admin-wsk","type":"blocks","created_at":"2026-01-12T16:28:20.305872992+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-qpy","depends_on_id":"wg-admin-0wc","type":"blocks","created_at":"2026-01-12T16:28:27.88358441+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-qpy","depends_on_id":"wg-admin-cwb","type":"blocks","created_at":"2026-01-12T16:28:27.890595849+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-qpy","depends_on_id":"wg-admin-2pl","type":"blocks","created_at":"2026-01-12T16:28:27.948214112+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-slj","title":"Refactor WireGuard scripts into modular architecture","description":"Refactor monolithic wireguard.sh into two separate scripts: wg-install.sh for initial setup, wg-client-manager for client operations. Use interactive 'read' prompts with 'WGI_' prefixed environment variable overrides. Add validation functions, security hardening, and remove all hardcoded sensitive information from repository.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:18.232667092+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:11:02.639140093+01:00","closed_at":"2026-01-12T17:11:02.639140093+01:00","close_reason":"Refactoring complete: wg-install.sh (921 lines) and wg-client-manager (545 lines) scripts have been created and are functional. wireguard.sh retained for backwards compatibility.","dependencies":[{"issue_id":"wg-admin-slj","depends_on_id":"wg-admin-abw","type":"blocks","created_at":"2026-01-12T16:28:21.930404739+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-slj","depends_on_id":"wg-admin-qpy","type":"blocks","created_at":"2026-01-12T16:28:21.936380993+01:00","created_by":"Calmcacil"},{"issue_id":"wg-admin-slj","depends_on_id":"wg-admin-0wc","type":"blocks","created_at":"2026-01-12T16:28:21.983754904+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-ti0","title":"Investigate q key behavior and parseHandshake bug","description":"Investigate q key behavior causing app to not return properly from client details. parseHandshake fix has been committed separately.","status":"open","priority":1,"issue_type":"bug","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:26:35.435240285+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:27:21.412723919+01:00"}
{"id":"wg-admin-tv6","title":"Add client delete functionality","description":"Implement delete client workflow with confirmation modal. Remove client config from server, delete client files, auto-backup before deletion, and reload WireGuard configuration.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.281557572+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:48:31.867154638+01:00","closed_at":"2026-01-12T17:48:31.867154638+01:00","close_reason":"Delete with confirmation modal implemented. Auto-backup before deletion, removes client configs and peer from WireGuard. Integrated with detail screen.","dependencies":[{"issue_id":"wg-admin-tv6","depends_on_id":"wg-admin-dd2","type":"blocks","created_at":"2026-01-12T17:04:36.207822184+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-u4f","title":"Research handshake timing discrepancy in wg show vs app","description":"Research why wg show wg0 shows different handshake timing than the app. Found that parseHandshake function only parses first time unit (e.g., '14 hours' from '14 hours, 24 minutes, 40 seconds ago'), ignoring subsequent units causing significant time discrepancy.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:14:02.054350138+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:14:09.570320895+01:00","closed_at":"2026-01-12T19:14:09.570320895+01:00","close_reason":"Research complete - identified parseHandshake only parsing first time unit in compound expressions"}
{"id":"wg-admin-wf1","title":"Create server and client config files","description":"Generate WireGuard configuration files for both server and client. Server config includes PublicKey and AllowedIPs. Client config includes PrivateKey, Address, DNS, Endpoint, and AllowedIPs. Use atomic writes (temp file + mv).","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.273615688+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:50:56.576191666+01:00","closed_at":"2026-01-12T17:50:56.576191666+01:00","close_reason":"Implemented WireGuard config file generation with atomic writes. Added GenerateServerConfig and GenerateClientConfig functions in internal/wireguard/config.go. Both functions use temp file + rename pattern for atomicity and set 0600 permissions.","dependencies":[{"issue_id":"wg-admin-wf1","depends_on_id":"wg-admin-o4o","type":"blocks","created_at":"2026-01-12T17:04:44.268995878+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-wjj","title":"Implement restore functionality","description":"Add restore capability to load backups from /etc/wg-admin/backups/. Include backup list view, restore confirmation, and pre-restore safety backup. Handle missing backups gracefully.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.29166861+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T18:06:07.558190874+01:00","closed_at":"2026-01-12T18:06:07.558190874+01:00","close_reason":"Restore functionality implemented in internal/backup/restore.go. ListBackups, RestoreBackup with validation. Restore screen with selectable table. Pre-restore safety backup. ReloadWireGuard integration. Successfully completes Phase 6.","dependencies":[{"issue_id":"wg-admin-wjj","depends_on_id":"wg-admin-11o","type":"blocks","created_at":"2026-01-12T17:04:36.234546234+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-wmn","title":"Fix parseHandshake to correctly parse compound time expressions","description":"Fix parseHandshake function in status.go to correctly parse compound time expressions like '14 hours, 24 minutes, 40 seconds ago'. Currently only parses first time unit, causing app to show incorrect handshake timing that differs from wg show output.","status":"closed","priority":1,"issue_type":"bug","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:14:02.056632886+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:14:41.087635401+01:00","closed_at":"2026-01-12T19:14:41.087635401+01:00","close_reason":"Fixed parseHandshake to accumulate all time units instead of returning early"}
{"id":"wg-admin-wmn","title":"Fix parseHandshake to correctly parse compound time expressions","description":"Fix parseHandshake function in status.go to correctly parse compound time expressions like '14 hours, 24 minutes, 40 seconds ago'. Currently only parses first time unit, causing app to show incorrect handshake timing that differs from wg show output.","status":"closed","priority":1,"issue_type":"bug","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:14:02.056632886+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:26:35.464413104+01:00","closed_at":"2026-01-12T19:26:35.464413104+01:00","close_reason":"Fixed parseHandshake to correctly parse all time units with number-unit pairing"}
{"id":"wg-admin-wod","title":"Create add client form with huh","description":"Implement form for adding new WireGuard clients using the huh library. Include fields for client name, DNS servers, and PSK toggle. Add validation for client name format, IP availability, and DNS format.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.272758265+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:56:47.982894819+01:00","closed_at":"2026-01-12T17:56:47.982894819+01:00","close_reason":"Implemented add client form using huh library with validation for client name, DNS servers, and PSK toggle. Added CreateClient function in wireguard package for key generation, IP allocation, config creation, QR code generation, and peer interface management. Updated main.go to handle 'a' key for add screen navigation.","dependencies":[{"issue_id":"wg-admin-wod","depends_on_id":"wg-admin-xum","type":"blocks","created_at":"2026-01-12T17:04:26.667835195+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-wsk","title":"Add configuration validation and syntax checking","description":"Implement validate_config_syntax() to check WireGuard config format before applying: verify [Interface] and [Peer] sections, check key format (44 base64 characters), validate IP addresses and CIDR notation, validate DNS format, ensure no duplicate public keys.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.159692055+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:38:35.611667005+01:00","closed_at":"2026-01-12T16:38:35.611667005+01:00","close_reason":"validate_config_syntax() implemented in previous task with full WireGuard config format validation: [Interface]/[Peer] sections, key format (44 base64 chars), IP/CIDR notation, DNS format, duplicate public key detection. Integrated into cmd_load_clients."}
{"id":"wg-admin-xni","title":"Update documentation with theme options","description":"Update README.md and documentation to include the new theme options (dracula, everforest) and how to switch themes using the THEME environment variable.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T19:07:40.300443242+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T19:10:22.220478311+01:00","closed_at":"2026-01-12T19:10:22.220478311+01:00","close_reason":"Documentation updated with theme options, build successful, theme switching logic verified"}

View File

@@ -47,11 +47,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
// Only quit on 'q' when on list screen
// Other screens handle 'q' to navigate back
if _, ok := m.currentScreen.(*screens.ListScreen); ok && (msg.String() == "q" || msg.String() == "ctrl+c") {
m.quitting = true
return m, tea.Quit
// Only quit on 'q' or ctrl+c when on list screen
// Other screens handle their own navigation keys
if msg.String() == "q" || msg.String() == "ctrl+c" {
if _, ok := m.currentScreen.(*screens.ListScreen); ok {
m.quitting = true
return m, tea.Quit
}
// For other screens, let them handle the key
}
switch msg.String() {
case "?":

View File

@@ -164,30 +164,39 @@ func parseHandshake(handshake string) time.Time {
parts := strings.Fields(handshake)
for i, part := range parts {
if strings.HasSuffix(part, "second") || strings.HasSuffix(part, "seconds") {
if val, err := strconv.Atoi(strings.TrimSuffix(part, "s")); err == nil {
totalDuration += time.Duration(val) * time.Second
// Clean up commas
cleanPart := strings.TrimSuffix(part, ",")
// Check if this part is a time unit
if strings.HasSuffix(cleanPart, "second") || strings.HasSuffix(cleanPart, "seconds") {
// The number is the previous part
if i > 0 {
if val, err := strconv.Atoi(parts[i-1]); err == nil {
totalDuration += time.Duration(val) * time.Second
}
}
}
if strings.HasSuffix(part, "minute") || strings.HasSuffix(part, "minutes") {
if val, err := strconv.Atoi(strings.TrimSuffix(part, "s")); err == nil {
totalDuration += time.Duration(val) * time.Minute
if strings.HasSuffix(cleanPart, "minute") || strings.HasSuffix(cleanPart, "minutes") {
if i > 0 {
if val, err := strconv.Atoi(parts[i-1]); err == nil {
totalDuration += time.Duration(val) * time.Minute
}
}
}
if strings.HasSuffix(part, "hour") || strings.HasSuffix(part, "hours") {
if val, err := strconv.Atoi(strings.TrimSuffix(part, "s")); err == nil {
totalDuration += time.Duration(val) * time.Hour
if strings.HasSuffix(cleanPart, "hour") || strings.HasSuffix(cleanPart, "hours") {
if i > 0 {
if val, err := strconv.Atoi(parts[i-1]); err == nil {
totalDuration += time.Duration(val) * time.Hour
}
}
}
if strings.HasSuffix(part, "day") || strings.HasSuffix(part, "days") {
if val, err := strconv.Atoi(strings.TrimSuffix(part, "s")); err == nil {
totalDuration += time.Duration(val) * 24 * time.Hour
if strings.HasSuffix(cleanPart, "day") || strings.HasSuffix(cleanPart, "days") {
if i > 0 {
if val, err := strconv.Atoi(parts[i-1]); err == nil {
totalDuration += time.Duration(val) * 24 * time.Hour
}
}
}
// Handle "ago" word
if i > 0 && (part == "ago" || part == "ago,") {
// Continue parsing time units
}
}
if totalDuration == 0 {