freeschema 1.0.4 → 1.0.6
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 +55 -4
- package/package.json +1 -1
- package/templates/docker-compose.yml +1 -0
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';
|
|
@@ -174,6 +212,11 @@ const COMPOSE_PATCHES = [
|
|
|
174
212
|
replace: '$1 user: "1883:1883"\n',
|
|
175
213
|
label: 'mosquitto user (1883:1883 to skip chown on Windows)',
|
|
176
214
|
},
|
|
215
|
+
{
|
|
216
|
+
find: /(--default-authentication-plugin=mysql_native_password)(?!\s*\n\s*--log-bin-trust)/,
|
|
217
|
+
replace: '$1\n --log-bin-trust-function-creators=1',
|
|
218
|
+
label: 'MySQL log_bin_trust_function_creators (allows stored functions/triggers)',
|
|
219
|
+
},
|
|
177
220
|
];
|
|
178
221
|
|
|
179
222
|
function patchComposeFile() {
|
|
@@ -340,6 +383,10 @@ async function cmdInit() {
|
|
|
340
383
|
const mqttPass = await prompt(' MQTT password', readEnvVar(envPath, 'MQTT_PASSWORD') || 'freeschema');
|
|
341
384
|
setEnvVar(envPath, 'MQTT_USERNAME', mqttUser);
|
|
342
385
|
setEnvVar(envPath, 'MQTT_PASSWORD', mqttPass);
|
|
386
|
+
// Write password.txt with correct hash for the chosen credentials
|
|
387
|
+
const pwDir = path.join(target, 'mosquitto', 'config');
|
|
388
|
+
if (!fs.existsSync(pwDir)) fs.mkdirSync(pwDir, { recursive: true });
|
|
389
|
+
fs.writeFileSync(path.join(pwDir, 'password.txt'), generateMosquittoPassword(mqttUser, mqttPass));
|
|
343
390
|
console.log(' MQTT configured (local) ✓');
|
|
344
391
|
} else {
|
|
345
392
|
console.log('\n External MQTT broker:');
|
|
@@ -476,7 +523,10 @@ function cmdStart() {
|
|
|
476
523
|
const profiles = [];
|
|
477
524
|
const flags = args.slice(1);
|
|
478
525
|
if (flags.includes('--local-db') || flags.includes('--db')) profiles.push('--profile', 'local-db');
|
|
479
|
-
if (flags.includes('--local-mqtt') || flags.includes('--mqtt'))
|
|
526
|
+
if (flags.includes('--local-mqtt') || flags.includes('--mqtt')) {
|
|
527
|
+
profiles.push('--profile', 'local-mqtt');
|
|
528
|
+
ensureMosquittoConfig(cwd);
|
|
529
|
+
}
|
|
480
530
|
|
|
481
531
|
const seedIdx = args.indexOf('--seed');
|
|
482
532
|
const seedFile = seedIdx !== -1 ? args[seedIdx + 1] : null;
|
|
@@ -492,13 +542,14 @@ function cmdStart() {
|
|
|
492
542
|
const env = readEnv();
|
|
493
543
|
const dbUser = env.DB_USER || env.MYSQL_USER || 'freeschema';
|
|
494
544
|
const dbPass = env.DB_PASSWORD || env.MYSQL_PASSWORD || 'Freeschema@123';
|
|
545
|
+
const rootPass = env.MYSQL_ROOT_PASSWORD || ('Admin@' + dbPass);
|
|
495
546
|
const dbName = env.DB_DATABASE || 'freeschema_db';
|
|
496
547
|
const container = getMysqlContainer();
|
|
497
548
|
if (!waitForMySQL(container, dbUser, dbPass)) die('MySQL did not become ready — check logs with `freeschema logs mysql-server`.');
|
|
498
549
|
console.log(` Seeding ${dbName} from ${seedFile}…`);
|
|
499
550
|
const result = spawnSync(
|
|
500
551
|
'docker',
|
|
501
|
-
['exec', '-i', container, 'mysql',
|
|
552
|
+
['exec', '-i', container, 'mysql', '-uroot', `-p${rootPass}`, dbName],
|
|
502
553
|
{ input: fs.readFileSync(seedFile), stdio: ['pipe', 'inherit', 'inherit'] }
|
|
503
554
|
);
|
|
504
555
|
if (result.error || result.status !== 0) die('Seed failed — check container logs.');
|
|
@@ -635,15 +686,15 @@ function cmdDbRestore() {
|
|
|
635
686
|
if (!fs.existsSync(sqlFile)) die(`File not found: ${sqlFile}`);
|
|
636
687
|
|
|
637
688
|
const env = readEnv();
|
|
638
|
-
const dbUser = env.DB_USER || env.MYSQL_USER || 'freeschema';
|
|
639
689
|
const dbPass = env.DB_PASSWORD || env.MYSQL_PASSWORD || 'Freeschema@123';
|
|
690
|
+
const rootPass = env.MYSQL_ROOT_PASSWORD || ('Admin@' + dbPass);
|
|
640
691
|
const dbName = env.DB_DATABASE || 'freeschema_db';
|
|
641
692
|
const container = getMysqlContainer();
|
|
642
693
|
|
|
643
694
|
console.log(`Restoring ${sqlFile} → ${dbName}…`);
|
|
644
695
|
const result = spawnSync(
|
|
645
696
|
'docker',
|
|
646
|
-
['exec', '-i', container, 'mysql',
|
|
697
|
+
['exec', '-i', container, 'mysql', '-uroot', `-p${rootPass}`, dbName],
|
|
647
698
|
{ input: fs.readFileSync(sqlFile), stdio: ['pipe', 'inherit', 'inherit'] }
|
|
648
699
|
);
|
|
649
700
|
if (result.error || result.status !== 0) die('Restore failed — check container logs.');
|
package/package.json
CHANGED
|
@@ -39,6 +39,7 @@ services:
|
|
|
39
39
|
user: "999:999"
|
|
40
40
|
command: >
|
|
41
41
|
--default-authentication-plugin=mysql_native_password
|
|
42
|
+
--log-bin-trust-function-creators=1
|
|
42
43
|
healthcheck:
|
|
43
44
|
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u${MYSQL_USER:-freeschema} -p${MYSQL_PASSWORD:-Freeschema@123} --silent"]
|
|
44
45
|
interval: 10s
|