freeschema 1.0.2 → 1.0.4

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,7 +114,14 @@ function generateJwt() {
114
114
  return crypto.randomBytes(32).toString('hex');
115
115
  }
116
116
 
117
- function httpsDownload(url, destStream) {
117
+ function fmtBytes(b) {
118
+ if (b >= 1073741824) return (b / 1073741824).toFixed(1) + ' GB';
119
+ if (b >= 1048576) return (b / 1048576).toFixed(1) + ' MB';
120
+ if (b >= 1024) return (b / 1024).toFixed(1) + ' KB';
121
+ return b + ' B';
122
+ }
123
+
124
+ function httpsDownload(url, destStream, onProgress) {
118
125
  return new Promise((resolve, reject) => {
119
126
  const follow = (u, redirects) => {
120
127
  if (redirects > 10) return reject(new Error('Too many redirects'));
@@ -123,6 +130,12 @@ function httpsDownload(url, destStream) {
123
130
  return follow(res.headers.location, redirects + 1);
124
131
  }
125
132
  if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
133
+ const total = parseInt(res.headers['content-length'], 10) || 0;
134
+ let received = 0;
135
+ res.on('data', chunk => {
136
+ received += chunk.length;
137
+ if (onProgress) onProgress(received, total);
138
+ });
126
139
  res.pipe(destStream);
127
140
  destStream.on('finish', resolve);
128
141
  destStream.on('error', reject);
@@ -132,6 +145,53 @@ function httpsDownload(url, destStream) {
132
145
  });
133
146
  }
134
147
 
148
+ function makeProgressBar(label) {
149
+ const BAR = 25;
150
+ return function onProgress(received, total) {
151
+ let line;
152
+ if (total) {
153
+ const pct = Math.min(100, Math.floor((received / total) * 100));
154
+ const filled = Math.floor((pct / 100) * BAR);
155
+ const bar = '█'.repeat(filled) + '░'.repeat(BAR - filled);
156
+ line = ` ${label} [${bar}] ${pct}% ${fmtBytes(received)} / ${fmtBytes(total)}`;
157
+ } else {
158
+ line = ` ${label} ${fmtBytes(received)} downloaded`;
159
+ }
160
+ process.stdout.write(`\r${line.padEnd(72)}`);
161
+ };
162
+ }
163
+
164
+ // Patches known issues in the local docker-compose.yml so existing installs
165
+ // are automatically healed on start/update without requiring a re-init.
166
+ const COMPOSE_PATCHES = [
167
+ {
168
+ find: /- \.\/mosquitto\/config:\/mosquitto\/config:ro/g,
169
+ replace: '- ./mosquitto/config:/mosquitto/config',
170
+ label: 'mosquitto config mount (removed :ro)',
171
+ },
172
+ {
173
+ find: /(image: eclipse-mosquitto:[^\n]+\n)(?! user:)/,
174
+ replace: '$1 user: "1883:1883"\n',
175
+ label: 'mosquitto user (1883:1883 to skip chown on Windows)',
176
+ },
177
+ ];
178
+
179
+ function patchComposeFile() {
180
+ const composePath = path.join(process.cwd(), 'docker-compose.yml');
181
+ if (!fs.existsSync(composePath)) return;
182
+ let content = fs.readFileSync(composePath, 'utf8');
183
+ let patched = false;
184
+ for (const { find, replace, label } of COMPOSE_PATCHES) {
185
+ if (find.test(content)) {
186
+ content = content.replace(find, replace);
187
+ patched = true;
188
+ console.log(` Patched docker-compose.yml: ${label}`);
189
+ }
190
+ find.lastIndex = 0;
191
+ }
192
+ if (patched) fs.writeFileSync(composePath, content);
193
+ }
194
+
135
195
  function runCompose(composeArgs, opts = {}) {
136
196
  const cwd = process.cwd();
137
197
  if (!fs.existsSync(path.join(cwd, 'docker-compose.yml'))) {
@@ -338,9 +398,11 @@ async function cmdInit() {
338
398
  const dest = path.join(seedDir, seedFileName);
339
399
  console.log(` Downloading → seed-db/${seedFileName}…`);
340
400
  try {
341
- await httpsDownload(seedUrl, fs.createWriteStream(dest));
401
+ await httpsDownload(seedUrl, fs.createWriteStream(dest), makeProgressBar('Downloading'));
402
+ process.stdout.write('\n');
342
403
  console.log(` Seed data seed-db/${seedFileName} ✓`);
343
404
  } catch (e) {
405
+ process.stdout.write('\n');
344
406
  try { fs.unlinkSync(dest); } catch {}
345
407
  console.log(` Seed data download failed (${e.message}) — skipped`);
346
408
  seedFileName = null;
@@ -402,6 +464,7 @@ function waitForMySQL(container, user, pass, maxAttempts = 30) {
402
464
  }
403
465
 
404
466
  function cmdStart() {
467
+ patchComposeFile();
405
468
  const cwd = process.cwd();
406
469
  const missing = ['.env', '.env.frontend'].filter(f => !fs.existsSync(path.join(cwd, f)));
407
470
  if (missing.length) {
@@ -470,6 +533,7 @@ function cmdPull() {
470
533
 
471
534
  function cmdUpdate() {
472
535
  console.log('Updating FreeSchema…\n');
536
+ patchComposeFile();
473
537
 
474
538
  // 1. Update the CLI itself via npm
475
539
  console.log('Step 1/2 — Updating freeschema CLI…');
@@ -536,9 +600,9 @@ function cmdDbSeed() {
536
600
  const name = path.basename(new URL(url.replace('%2540', '%40')).pathname) || 'seed.sql';
537
601
  const dest = path.join(seedDir, name);
538
602
  console.log(`Downloading → seed-db/${name}…`);
539
- httpsDownload(url, fs.createWriteStream(dest))
540
- .then(() => console.log('Done. Run `freeschema start --local-db` to apply on first boot.'))
541
- .catch(e => { try { fs.unlinkSync(dest); } catch {} die(e.message); });
603
+ httpsDownload(url, fs.createWriteStream(dest), makeProgressBar('Downloading'))
604
+ .then(() => { process.stdout.write('\n'); console.log('Done. Run `freeschema start --local-db` to apply on first boot.'); })
605
+ .catch(e => { process.stdout.write('\n'); try { fs.unlinkSync(dest); } catch {} die(e.message); });
542
606
  return;
543
607
  }
544
608
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freeschema",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "FreeSchema — self-hosted deployment CLI",
5
5
  "keywords": [
6
6
  "freeschema",
@@ -51,10 +51,11 @@ 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:
57
- - ./mosquitto/config:/mosquitto/config:ro
58
+ - ./mosquitto/config:/mosquitto/config
58
59
  - ./mosquitto/data:/mosquitto/data
59
60
  - ./mosquitto/log:/mosquitto/log
60
61
  networks:
@@ -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==