@noforeignland/signalk-to-noforeignland 1.1.0-beta.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  1.1.0
2
+ * CHANGE: Removed postinstall script - SignalK server will use --ignore-scripts (issue #2181). Runtime PluginCleanup handles all migration.
2
3
  * CHANGE: Refactored Project structure, see PROJECT_STRUCTURE.md
3
4
  * CHANGE: Use phrase GNSS instead of GPS - Thanks Piotr
5
+ * BUGFIX: Removed unreliable DNS-based internet connectivity test (beta.11) - API retry logic is sufficient
6
+ * BUGFIX: Reverted to beta.3 working configuration (beta.10) - undoing all failed schema experiments from beta.4-9
7
+ * CHANGE: Improved cleanup error handling - only shows error after all retry attempts fail
8
+ * CHANGE: Added installed plugins logging for better debugging
9
+ * CHANGE: Platform-specific cleanup instructions (Cerbo GX vs standard)
4
10
 
5
11
  1.0.1
6
12
  * CHANGE: Cleanup previous installs and migrate plugin config, removes depricated old plugins.
package/README.md CHANGED
@@ -1,9 +1,6 @@
1
1
  # Signal K To Noforeignland
2
2
  Effortlessly log your boat's movement to **noforeignland.com**
3
3
 
4
- ## Important for 0.1.x users
5
- Upgrade to >1.1.0 by installing this plugin from the Appstore again. Your old config will be migrated and old plugins will be automatically removed.
6
-
7
4
  ## Features
8
5
  * Automatically log your position to noforeignland.com
9
6
  * Send detailed tracks to log your entire trip and not just your final position
@@ -14,8 +11,9 @@ Upgrade to >1.1.0 by installing this plugin from the Appstore again. Your old co
14
11
  * SK data paths about the plugin status for your own dashboard or Node Red coding
15
12
 
16
13
  ## Issues
17
- * Server -> Plugin Config -> Signal K to Noforeignland -> Enable debug log (top right)
18
- * Report issues on GitHub (https://github.com/noforeignland/nfl-signalk/issues)
14
+ * Enable debug logging: Server -> Plugin Config -> Signal K to Noforeignland -> Enable debug log (top right)
15
+ * Check logs: Server -> Server Log (in SignalK web UI)
16
+ * Report issues on [GitHub](https://github.com/noforeignland/nfl-signalk/issues)
19
17
 
20
18
  ## Requirements
21
19
  * An internet connection is required in order to update noforeignland.com
@@ -44,8 +42,6 @@ noforeignland.source - string - string - data source of
44
42
  notifications.noforeignland.status_boolean - json object - auto created
45
43
  ```
46
44
 
47
- https://github.com/noforeignland/nfl-signalk/issues
48
-
49
45
  # Virctron Cerbo GX Users
50
46
 
51
47
  ## Limited storage
@@ -84,9 +84,6 @@ chown -R signalk:signalk /data/conf/signalk/*
84
84
  ```
85
85
 
86
86
 
87
- ----------------------------------------
88
-
89
-
90
87
  # dev tree (unstable) install - NOT RECOMMENDED
91
88
 
92
89
  1. Backup as above
@@ -111,6 +108,20 @@ chown -R signalk:signalk /data/conf/signalk/*
111
108
  ```
112
109
 
113
110
  3. Restart Server & Check logs
111
+ ```
112
+ svc -t /service/signalk-server
113
+ ```
114
+
115
+ # Manual Fixes
116
+
117
+ ## WARNING: found multiple copies of plugin with id signalk-to-noforeignland at /data/conf/signalk/node_modules/ and /data/conf/signalk/node_modules/
118
+
119
+ ```
120
+ cd /data/conf/signalk && npm uninstall signalk-to-noforeignland signalk-to-nfl
121
+ ```
122
+
123
+ and than
124
+
114
125
  ```
115
126
  svc -t /service/signalk-server
116
127
  ```
package/index.js CHANGED
@@ -202,17 +202,8 @@ class SignalkToNoforeignland {
202
202
  if (!hasTrack) {
203
203
  return;
204
204
  }
205
-
206
- // Test internet connection
207
- const hasInternet = await this.trackSender.testInternet();
208
- if (!hasInternet) {
209
- const errorMsg = 'No internet connection detected. Unable to send tracking data to NFL. DNS lookups failed - check your internet connection.';
210
- this.app.debug(errorMsg);
211
- this.setPluginError(errorMsg);
212
- return;
213
- }
214
-
215
- // Send track data
205
+
206
+ // Send track data (has built-in retry logic)
216
207
  const success = await this.trackSender.sendTrack();
217
208
 
218
209
  if (success) {
@@ -13,37 +13,130 @@ class PluginCleanup {
13
13
  try {
14
14
  // Detect SignalK directory (standard or Victron Cerbo)
15
15
  const victronPath = '/data/conf/signalk';
16
- const standardPath = process.env.SIGNALK_NODE_CONFIG_DIR ||
16
+ const standardPath = process.env.SIGNALK_NODE_CONFIG_DIR ||
17
17
  path.join(process.env.HOME || process.env.USERPROFILE, '.signalk');
18
-
18
+
19
19
  const configDir = fs.existsSync(victronPath) ? victronPath : standardPath;
20
20
  this.app.debug(`Using SignalK directory: ${configDir}`);
21
-
21
+
22
22
  const configPath = path.join(configDir, 'plugin-config-data');
23
-
23
+
24
24
  // 1. Config Migration - only from signalk-to-noforeignland
25
25
  await this.migrateConfig(configPath);
26
-
27
- // 2. Check and remove old plugins
28
- await this.removeOldPlugins(configDir);
29
-
26
+
27
+ // 2. Verify what plugins are actually present
28
+ this.logInstalledPlugins(configDir);
29
+
30
+ // 3. Check and remove old plugins
31
+ return await this.removeOldPlugins(configDir);
32
+
30
33
  } catch (err) {
31
34
  this.app.debug('Error during old plugin cleanup:', err.message);
35
+ return null;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Log which SignalK NFL plugins are currently installed (for debugging)
41
+ */
42
+ logInstalledPlugins(configDir) {
43
+ const nodeModulesDir = path.join(configDir, 'node_modules');
44
+ const pluginsToCheck = [
45
+ '@noforeignland/signalk-to-noforeignland',
46
+ 'signalk-to-noforeignland',
47
+ 'signalk-to-nfl'
48
+ ];
49
+
50
+ const found = [];
51
+ for (const pluginName of pluginsToCheck) {
52
+ const pluginPath = path.join(nodeModulesDir, pluginName);
53
+ if (fs.existsSync(pluginPath)) {
54
+ const packageJsonPath = path.join(pluginPath, 'package.json');
55
+ let version = 'unknown';
56
+ try {
57
+ if (fs.existsSync(packageJsonPath)) {
58
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
59
+ version = packageJson.version;
60
+ }
61
+ } catch (e) {
62
+ // Ignore version read errors
63
+ }
64
+ found.push(`${pluginName}@${version}`);
65
+ }
66
+ }
67
+
68
+ if (found.length > 0) {
69
+ this.app.debug(`Installed NFL plugins: ${found.join(', ')}`);
32
70
  }
33
71
  }
34
72
 
35
73
  /**
36
- * Migrate config from old plugin to new scoped version
74
+ * Migrate config from old plugin ID to new plugin ID
75
+ *
76
+ * Handles config migration from:
77
+ * 1. Old unscoped plugin "signalk-to-noforeignland" (v0.1.x)
78
+ * 2. Beta versions with wrong plugin ID (v1.1.0-beta.1/2/3)
79
+ *
80
+ * Both used config filename: "signalk-to-noforeignland.json"
81
+ * New version uses: "@noforeignland-signalk-to-noforeignland.json"
82
+ *
83
+ * Since both sources use the same filename, we simply copy if it exists.
37
84
  */
38
85
  async migrateConfig(configPath) {
39
86
  const oldConfigFile = path.join(configPath, 'signalk-to-noforeignland.json');
40
87
  const newConfigFile = path.join(configPath, '@noforeignland-signalk-to-noforeignland.json');
41
-
88
+
89
+ // Only migrate if old config exists and new config doesn't
42
90
  if (fs.existsSync(oldConfigFile) && !fs.existsSync(newConfigFile)) {
43
- this.app.debug('Migrating configuration from old plugin "signalk-to-noforeignland"...');
44
- fs.copyFileSync(oldConfigFile, newConfigFile);
45
- fs.copyFileSync(oldConfigFile, `${oldConfigFile}.backup`);
46
- this.app.debug('✓ Configuration migrated successfully');
91
+ this.app.debug('Migrating configuration from old plugin to new scoped plugin...');
92
+ this.app.debug(` Source: ${oldConfigFile}`);
93
+ this.app.debug(` Target: ${newConfigFile}`);
94
+
95
+ try {
96
+ // Copy to new location
97
+ fs.copyFileSync(oldConfigFile, newConfigFile);
98
+
99
+ // Create backup of old config
100
+ const backupFile = `${oldConfigFile}.backup-${Date.now()}`;
101
+ fs.copyFileSync(oldConfigFile, backupFile);
102
+
103
+ this.app.debug('✓ Configuration successfully migrated');
104
+ this.app.debug(` Backup saved: ${backupFile}`);
105
+ } catch (err) {
106
+ this.app.debug(`⨯ Config migration failed: ${err.message}`);
107
+ this.app.debug(' You may need to reconfigure the plugin manually');
108
+ }
109
+ } else if (fs.existsSync(newConfigFile)) {
110
+ this.app.debug('Configuration already in new location');
111
+
112
+ // Check if config has incorrect "configuration" wrapper and fix it
113
+ try {
114
+ const configData = JSON.parse(fs.readFileSync(newConfigFile, 'utf8'));
115
+ if (configData.configuration && typeof configData.configuration === 'object') {
116
+ this.app.debug('Detected nested "configuration" wrapper, unwrapping...');
117
+
118
+ // Unwrap: move properties from configuration object to root
119
+ const unwrapped = {
120
+ ...configData.configuration,
121
+ enabled: configData.enabled,
122
+ enableLogging: configData.enableLogging,
123
+ enableDebug: configData.enableDebug
124
+ };
125
+
126
+ // Backup before fixing
127
+ const fixBackupFile = `${newConfigFile}.backup-unwrap-${Date.now()}`;
128
+ fs.copyFileSync(newConfigFile, fixBackupFile);
129
+
130
+ // Write fixed config
131
+ fs.writeFileSync(newConfigFile, JSON.stringify(unwrapped, null, 2));
132
+ this.app.debug('✓ Configuration unwrapped successfully');
133
+ this.app.debug(` Backup: ${fixBackupFile}`);
134
+ }
135
+ } catch (err) {
136
+ this.app.debug(`⨯ Could not check/fix config structure: ${err.message}`);
137
+ }
138
+ } else {
139
+ this.app.debug('No old configuration found, first-time setup');
47
140
  }
48
141
  }
49
142
 
@@ -87,16 +180,11 @@ class PluginCleanup {
87
180
  this.app.debug('All old plugins removed successfully');
88
181
  return 'all_removed';
89
182
  }
90
-
91
- // Show warning for plugins that couldn't be removed immediately
183
+
184
+ // Don't show error immediately - log that we're retrying
92
185
  const stillPresentNames = stillPresent.map(p => `"${p.name}"`).join(' and ');
93
- const stillPresentCmd = stillPresent.map(p => p.name).join(' ');
94
-
95
- this.app.setPluginError(
96
- `Old plugin(s) ${stillPresentNames} still installed. ` +
97
- `Will retry removal, or uninstall manually: cd ${configDir} && npm uninstall ${stillPresentCmd}`
98
- );
99
-
186
+ this.app.debug(`Old plugin(s) ${stillPresentNames} still present, will retry removal...`);
187
+
100
188
  // Delayed removal attempts (multiple tries with increasing delays)
101
189
  return new Promise((resolve) => {
102
190
  const delays = [5000, 15000, 30000]; // 5s, 15s, 30s
@@ -104,12 +192,22 @@ class PluginCleanup {
104
192
 
105
193
  const attemptRemoval = async () => {
106
194
  if (attemptIndex >= delays.length) {
107
- // Final attempt failed
195
+ // Final attempt failed - NOW show error only if plugins still exist
108
196
  const remaining = stillPresent.filter(p => fs.existsSync(p.dir));
109
197
  if (remaining.length > 0) {
110
- const remainingNames = remaining.map(p => p.name).join(' ');
111
- this.app.debug(`Could not remove old plugins after multiple attempts: ${remainingNames}`);
112
- this.app.debug(`Please manually run: cd ${configDir} && npm uninstall ${remainingNames}`);
198
+ const remainingNames = remaining.map(p => `"${p.name}"`).join(' and ');
199
+ const remainingCmd = remaining.map(p => p.name).join(' ');
200
+
201
+ this.app.debug(`Could not remove old plugins after ${delays.length} attempts: ${remainingNames}`);
202
+
203
+ // Show error with platform-specific commands
204
+ const isVictronCerbo = configDir === '/data/conf/signalk';
205
+ const cmdPrefix = isVictronCerbo ? 'On Victron Cerbo GX, ' : '';
206
+
207
+ this.app.setPluginError(
208
+ `${cmdPrefix}Old plugin(s) ${remainingNames} detected. ` +
209
+ `Manual removal required: cd ${configDir} && npm uninstall ${remainingCmd}`
210
+ );
113
211
  resolve('partial_removal');
114
212
  } else {
115
213
  this.app.debug('All old plugins eventually removed');
@@ -36,44 +36,6 @@ class TrackSender {
36
36
  }
37
37
  }
38
38
 
39
- /**
40
- * Test internet connectivity
41
- */
42
- async testInternet() {
43
- const dns = require('dns').promises;
44
-
45
- this.app.debug('testing internet connection');
46
-
47
- const timeoutMs = this.options.internetTestTimeout || 2000;
48
- this.app.debug(`Using internet test timeout: ${timeoutMs}ms`);
49
-
50
- const dnsServers = [
51
- { name: 'Google DNS', ip: '8.8.8.8' },
52
- { name: 'Cloudflare DNS', ip: '1.1.1.1' }
53
- ];
54
-
55
- for (const server of dnsServers) {
56
- try {
57
- const startTime = Date.now();
58
- const result = await Promise.race([
59
- dns.reverse(server.ip),
60
- new Promise((_, reject) =>
61
- setTimeout(() => reject(new Error('DNS timeout')), timeoutMs)
62
- )
63
- ]);
64
- const elapsed = Date.now() - startTime;
65
-
66
- this.app.debug(`internet connection = true, ${server.name} (${server.ip}) is reachable (took ${elapsed}ms)`);
67
- return true;
68
- } catch (err) {
69
- this.app.debug(`${server.name} (${server.ip}) not reachable:`, err.message);
70
- }
71
- }
72
-
73
- this.app.debug(`internet connection = false, no public DNS servers reachable (timeout was ${timeoutMs}ms)`);
74
- return false;
75
- }
76
-
77
39
  /**
78
40
  * Check if track file exists and has content
79
41
  */
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "signalk": {
4
4
  "id": "@noforeignland/signalk-to-noforeignland"
5
5
  },
6
- "version": "1.1.0-beta.3",
6
+ "version": "1.1.0",
7
7
  "description": "SignalK track logger to noforeignland.com",
8
8
  "main": "index.js",
9
9
  "keywords": [
@@ -24,8 +24,7 @@
24
24
  "node": ">=18.0.0"
25
25
  },
26
26
  "scripts": {
27
- "test": "echo \"No tests specified\" && exit 0",
28
- "postinstall": "node cleanup-old-plugin.js"
27
+ "test": "echo \"No tests specified\" && exit 0"
29
28
  },
30
29
  "dependencies": {
31
30
  "cron": "^2.1.0",
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Cleanup script for old plugin versions
5
- * This runs as a standalone script during npm postinstall
6
- * NOT a class - just a simple executable script
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
-
12
- // Detect SignalK directory (standard ~/.signalk or Victron Cerbo /data/conf/signalk)
13
- function getSignalKDir() {
14
- const victronPath = '/data/conf/signalk';
15
- const homePath = path.join(process.env.HOME || '', '.signalk');
16
-
17
- // Check Victron path first
18
- if (fs.existsSync(victronPath)) {
19
- return victronPath;
20
- }
21
-
22
- return homePath;
23
- }
24
-
25
- const signalkDir = getSignalKDir();
26
- console.log(`Using SignalK directory: ${signalkDir}`);
27
-
28
- // 1. Migration (only for signalk-to-noforeignland config)
29
- try {
30
- const configPath = path.join(signalkDir, 'plugin-config-data');
31
- const oldConfig = path.join(configPath, 'signalk-to-noforeignland.json');
32
- const newConfig = path.join(configPath, '@noforeignland-signalk-to-noforeignland.json');
33
-
34
- if (fs.existsSync(oldConfig) && !fs.existsSync(newConfig)) {
35
- fs.copyFileSync(oldConfig, newConfig);
36
- fs.copyFileSync(oldConfig, `${oldConfig}.backup`);
37
- console.log('✓ Configuration migrated');
38
- }
39
- } catch (e) {
40
- console.warn('Could not migrate config:', e.message);
41
- }
42
-
43
- // 2. Uninstall old plugins (with delay so npm can finish)
44
- setTimeout(() => {
45
- try {
46
- const oldPlugins = [
47
- { dir: path.join(signalkDir, 'node_modules', 'signalk-to-noforeignland'), name: 'signalk-to-noforeignland' },
48
- { dir: path.join(signalkDir, 'node_modules', 'signalk-to-nfl'), name: 'signalk-to-nfl' }
49
- ];
50
-
51
- let removedAny = false;
52
- const failedPlugins = [];
53
-
54
- for (const plugin of oldPlugins) {
55
- if (fs.existsSync(plugin.dir)) {
56
- try {
57
- console.log(`Removing old plugin "${plugin.name}"...`);
58
- // Direct deletion is safer than npm uninstall during installation
59
- fs.rmSync(plugin.dir, { recursive: true, force: true });
60
- console.log(`✓ Old plugin "${plugin.name}" removed`);
61
- removedAny = true;
62
- } catch (e) {
63
- console.warn(`Could not remove "${plugin.name}":`, e.message);
64
- failedPlugins.push(plugin.name);
65
- }
66
- }
67
- }
68
-
69
- if (failedPlugins.length > 0) {
70
- const uninstallCmd = failedPlugins.join(' ');
71
- console.warn(`Please run manually: npm uninstall ${uninstallCmd}`);
72
- }
73
-
74
- if (!removedAny && failedPlugins.length === 0) {
75
- console.log('No old plugins found - already clean!');
76
- }
77
- } catch (e) {
78
- console.warn('Error during cleanup:', e.message);
79
- }
80
- }, 2000); // Wait 2 seconds for npm to finish
package/doc/dev DELETED
File without changes