freeschema 1.0.3 → 1.0.5

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/bin/freeschema.js CHANGED
@@ -114,6 +114,44 @@ function generateJwt() {
114
114
  return crypto.randomBytes(32).toString('hex');
115
115
  }
116
116
 
117
+ // Mosquitto 2.0 $7$ PBKDF2-HMAC-SHA512 password entry
118
+ function generateMosquittoPassword(username, password) {
119
+ const iterations = 101;
120
+ const salt = crypto.randomBytes(12);
121
+ const hash = crypto.pbkdf2Sync(password, salt, iterations, 64, 'sha512');
122
+ return `${username}:$7$${iterations}$${salt.toString('base64')}$${hash.toString('base64')}\n`;
123
+ }
124
+
125
+ function ensureMosquittoConfig(cwd) {
126
+ const dir = (sub) => path.join(cwd, 'mosquitto', sub);
127
+ ['config', 'data', 'log'].forEach(d => {
128
+ if (!fs.existsSync(dir(d))) fs.mkdirSync(dir(d), { recursive: true });
129
+ });
130
+
131
+ const confDest = path.join(dir('config'), 'mosquitto.conf');
132
+ if (!fs.existsSync(confDest)) {
133
+ const confSrc = path.join(TEMPLATES_DIR, 'mosquitto', 'config', 'mosquitto.conf');
134
+ if (fs.existsSync(confSrc)) fs.copyFileSync(confSrc, confDest);
135
+ else fs.writeFileSync(confDest, [
136
+ 'listener 1883', 'allow_anonymous false',
137
+ 'password_file /mosquitto/config/password.txt', '',
138
+ 'listener 9001', 'protocol websockets', 'allow_anonymous false', '',
139
+ 'persistence true', 'persistence_location /mosquitto/data/',
140
+ 'log_dest file /mosquitto/log/mosquitto.log', '',
141
+ ].join('\n'));
142
+ console.log(' Created mosquitto/config/mosquitto.conf');
143
+ }
144
+
145
+ const pwDest = path.join(dir('config'), 'password.txt');
146
+ if (!fs.existsSync(pwDest)) {
147
+ const env = readEnv();
148
+ const mqttUser = env.MQTT_USERNAME || 'freeschema';
149
+ const mqttPass = env.MQTT_PASSWORD || 'freeschema';
150
+ fs.writeFileSync(pwDest, generateMosquittoPassword(mqttUser, mqttPass));
151
+ console.log(' Created mosquitto/config/password.txt');
152
+ }
153
+ }
154
+
117
155
  function fmtBytes(b) {
118
156
  if (b >= 1073741824) return (b / 1073741824).toFixed(1) + ' GB';
119
157
  if (b >= 1048576) return (b / 1048576).toFixed(1) + ' MB';
@@ -161,6 +199,37 @@ function makeProgressBar(label) {
161
199
  };
162
200
  }
163
201
 
202
+ // Patches known issues in the local docker-compose.yml so existing installs
203
+ // are automatically healed on start/update without requiring a re-init.
204
+ const COMPOSE_PATCHES = [
205
+ {
206
+ find: /- \.\/mosquitto\/config:\/mosquitto\/config:ro/g,
207
+ replace: '- ./mosquitto/config:/mosquitto/config',
208
+ label: 'mosquitto config mount (removed :ro)',
209
+ },
210
+ {
211
+ find: /(image: eclipse-mosquitto:[^\n]+\n)(?! user:)/,
212
+ replace: '$1 user: "1883:1883"\n',
213
+ label: 'mosquitto user (1883:1883 to skip chown on Windows)',
214
+ },
215
+ ];
216
+
217
+ function patchComposeFile() {
218
+ const composePath = path.join(process.cwd(), 'docker-compose.yml');
219
+ if (!fs.existsSync(composePath)) return;
220
+ let content = fs.readFileSync(composePath, 'utf8');
221
+ let patched = false;
222
+ for (const { find, replace, label } of COMPOSE_PATCHES) {
223
+ if (find.test(content)) {
224
+ content = content.replace(find, replace);
225
+ patched = true;
226
+ console.log(` Patched docker-compose.yml: ${label}`);
227
+ }
228
+ find.lastIndex = 0;
229
+ }
230
+ if (patched) fs.writeFileSync(composePath, content);
231
+ }
232
+
164
233
  function runCompose(composeArgs, opts = {}) {
165
234
  const cwd = process.cwd();
166
235
  if (!fs.existsSync(path.join(cwd, 'docker-compose.yml'))) {
@@ -309,6 +378,10 @@ async function cmdInit() {
309
378
  const mqttPass = await prompt(' MQTT password', readEnvVar(envPath, 'MQTT_PASSWORD') || 'freeschema');
310
379
  setEnvVar(envPath, 'MQTT_USERNAME', mqttUser);
311
380
  setEnvVar(envPath, 'MQTT_PASSWORD', mqttPass);
381
+ // Write password.txt with correct hash for the chosen credentials
382
+ const pwDir = path.join(target, 'mosquitto', 'config');
383
+ if (!fs.existsSync(pwDir)) fs.mkdirSync(pwDir, { recursive: true });
384
+ fs.writeFileSync(path.join(pwDir, 'password.txt'), generateMosquittoPassword(mqttUser, mqttPass));
312
385
  console.log(' MQTT configured (local) ✓');
313
386
  } else {
314
387
  console.log('\n External MQTT broker:');
@@ -433,6 +506,7 @@ function waitForMySQL(container, user, pass, maxAttempts = 30) {
433
506
  }
434
507
 
435
508
  function cmdStart() {
509
+ patchComposeFile();
436
510
  const cwd = process.cwd();
437
511
  const missing = ['.env', '.env.frontend'].filter(f => !fs.existsSync(path.join(cwd, f)));
438
512
  if (missing.length) {
@@ -444,7 +518,10 @@ function cmdStart() {
444
518
  const profiles = [];
445
519
  const flags = args.slice(1);
446
520
  if (flags.includes('--local-db') || flags.includes('--db')) profiles.push('--profile', 'local-db');
447
- if (flags.includes('--local-mqtt') || flags.includes('--mqtt')) profiles.push('--profile', 'local-mqtt');
521
+ if (flags.includes('--local-mqtt') || flags.includes('--mqtt')) {
522
+ profiles.push('--profile', 'local-mqtt');
523
+ ensureMosquittoConfig(cwd);
524
+ }
448
525
 
449
526
  const seedIdx = args.indexOf('--seed');
450
527
  const seedFile = seedIdx !== -1 ? args[seedIdx + 1] : null;
@@ -501,6 +578,7 @@ function cmdPull() {
501
578
 
502
579
  function cmdUpdate() {
503
580
  console.log('Updating FreeSchema…\n');
581
+ patchComposeFile();
504
582
 
505
583
  // 1. Update the CLI itself via npm
506
584
  console.log('Step 1/2 — Updating freeschema CLI…');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freeschema",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "FreeSchema — self-hosted deployment CLI",
5
5
  "keywords": [
6
6
  "freeschema",
@@ -51,6 +51,7 @@ services:
51
51
  container_name: ${CONTAINER_PREFIX:-}mqtt-broker
52
52
  profiles:
53
53
  - local-mqtt
54
+ user: "1883:1883"
54
55
  ports:
55
56
  - "${MQTT_EXTERNAL_PORT:-1884}:1883"
56
57
  volumes:
@@ -1,11 +1,11 @@
1
- listener 1883
2
- allow_anonymous false
3
- password_file /mosquitto/config/password.txt
4
-
5
- listener 9001
6
- protocol websockets
7
- allow_anonymous false
8
-
9
- persistence true
10
- persistence_location /mosquitto/data/
11
- log_dest file /mosquitto/log/mosquitto.log
1
+ listener 1883
2
+ allow_anonymous false
3
+ password_file /mosquitto/config/password.txt
4
+
5
+ listener 9001
6
+ protocol websockets
7
+ allow_anonymous false
8
+
9
+ persistence true
10
+ persistence_location /mosquitto/data/
11
+ log_dest file /mosquitto/log/mosquitto.log
@@ -0,0 +1 @@
1
+ freeschema:$7$101$orXdSDv8OTk6tUnW$XhsqgkbuoDKh0PqtwNBfXZxyPVSKkv7zgHrjAKL5uBnl79kX0AXrSn8o4TWbeVYFVEE4q+r2IiadBPAKORw4kQ==