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
|
|
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
|
@@ -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
|
|
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==
|