freeschema 1.0.6 → 1.0.7
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 +148 -3
- package/package.json +1 -1
- package/templates/.env.example +2 -2
package/bin/freeschema.js
CHANGED
|
@@ -6,6 +6,7 @@ const { spawnSync } = require('child_process');
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const https = require('https');
|
|
9
|
+
const http = require('http');
|
|
9
10
|
const crypto = require('crypto');
|
|
10
11
|
const readline = require('readline');
|
|
11
12
|
|
|
@@ -510,7 +511,7 @@ function waitForMySQL(container, user, pass, maxAttempts = 30) {
|
|
|
510
511
|
return false;
|
|
511
512
|
}
|
|
512
513
|
|
|
513
|
-
function cmdStart() {
|
|
514
|
+
async function cmdStart() {
|
|
514
515
|
patchComposeFile();
|
|
515
516
|
const cwd = process.cwd();
|
|
516
517
|
const missing = ['.env', '.env.frontend'].filter(f => !fs.existsSync(path.join(cwd, f)));
|
|
@@ -560,6 +561,18 @@ function cmdStart() {
|
|
|
560
561
|
console.log(' Status: freeschema status');
|
|
561
562
|
console.log(' Logs: freeschema logs');
|
|
562
563
|
console.log(' Stop: freeschema stop');
|
|
564
|
+
|
|
565
|
+
// Auto-setup credentials if CLIENT_SECRET is missing or placeholder
|
|
566
|
+
const envAfterStart = readEnv();
|
|
567
|
+
const clientSecret = envAfterStart.CLIENT_SECRET || '';
|
|
568
|
+
const isPlaceholder = clientSecret === '' || clientSecret === 'sk_test_placeholder';
|
|
569
|
+
if (isPlaceholder) {
|
|
570
|
+
console.log('\n─────────────────────────────────────────');
|
|
571
|
+
console.log(' CLIENT_SECRET is not configured.');
|
|
572
|
+
console.log(' Setting up admin credentials now…');
|
|
573
|
+
console.log('─────────────────────────────────────────');
|
|
574
|
+
await cmdSetupCredentials();
|
|
575
|
+
}
|
|
563
576
|
}
|
|
564
577
|
|
|
565
578
|
// ── other commands ────────────────────────────────────────────────────────────
|
|
@@ -701,6 +714,135 @@ function cmdDbRestore() {
|
|
|
701
714
|
console.log('Restore complete.');
|
|
702
715
|
}
|
|
703
716
|
|
|
717
|
+
// ── setup-credentials ─────────────────────────────────────────────────────────
|
|
718
|
+
|
|
719
|
+
function httpPost(url, body) {
|
|
720
|
+
return new Promise((resolve, reject) => {
|
|
721
|
+
const parsed = new URL(url);
|
|
722
|
+
const payload = JSON.stringify(body);
|
|
723
|
+
const opts = {
|
|
724
|
+
hostname: parsed.hostname,
|
|
725
|
+
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
|
|
726
|
+
path: parsed.pathname,
|
|
727
|
+
method: 'POST',
|
|
728
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
729
|
+
};
|
|
730
|
+
const lib = parsed.protocol === 'https:' ? https : http;
|
|
731
|
+
const req = lib.request(opts, res => {
|
|
732
|
+
let data = '';
|
|
733
|
+
res.on('data', chunk => { data += chunk; });
|
|
734
|
+
res.on('end', () => {
|
|
735
|
+
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
|
|
736
|
+
catch { resolve({ status: res.statusCode, body: data }); }
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
req.on('error', reject);
|
|
740
|
+
req.write(payload);
|
|
741
|
+
req.end();
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function httpPostWithToken(url, token) {
|
|
746
|
+
return new Promise((resolve, reject) => {
|
|
747
|
+
const parsed = new URL(url);
|
|
748
|
+
const opts = {
|
|
749
|
+
hostname: parsed.hostname,
|
|
750
|
+
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
|
|
751
|
+
path: parsed.pathname,
|
|
752
|
+
method: 'POST',
|
|
753
|
+
headers: { 'Authorization': `Bearer ${token}`, 'Content-Length': 0 },
|
|
754
|
+
};
|
|
755
|
+
const lib = parsed.protocol === 'https:' ? https : http;
|
|
756
|
+
const req = lib.request(opts, res => {
|
|
757
|
+
let data = '';
|
|
758
|
+
res.on('data', chunk => { data += chunk; });
|
|
759
|
+
res.on('end', () => {
|
|
760
|
+
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
|
|
761
|
+
catch { resolve({ status: res.statusCode, body: data }); }
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
req.on('error', reject);
|
|
765
|
+
req.end();
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
async function cmdSetupCredentials() {
|
|
770
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
771
|
+
if (!fs.existsSync(envPath)) die('.env not found. Run `freeschema init` first.');
|
|
772
|
+
|
|
773
|
+
const env = readEnv();
|
|
774
|
+
const wicoPort = env.WICO_PORT || '7071';
|
|
775
|
+
const baseUrl = `http://localhost:${wicoPort}/api`;
|
|
776
|
+
|
|
777
|
+
console.log(`\nSetting up CLIENT_ID and CLIENT_SECRET`);
|
|
778
|
+
console.log(`Using WICO server at ${baseUrl}\n`);
|
|
779
|
+
|
|
780
|
+
// Step 1: login
|
|
781
|
+
const email = await prompt('Admin email');
|
|
782
|
+
const password = await promptSecret('Admin password');
|
|
783
|
+
if (!email || !password) die('Email and password are required.');
|
|
784
|
+
|
|
785
|
+
process.stdout.write('\n[1/3] Logging in… ');
|
|
786
|
+
let loginRes;
|
|
787
|
+
try {
|
|
788
|
+
loginRes = await httpPost(`${baseUrl}/auth/login`, { email, password, application: 'boomconsole' });
|
|
789
|
+
} catch (e) {
|
|
790
|
+
die(`Could not reach WICO server at ${baseUrl}/auth/login\nIs FreeSchema running? Try: freeschema start`);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (loginRes.status !== 200 || !loginRes.body) {
|
|
794
|
+
die(`Login failed (HTTP ${loginRes.status}): ${JSON.stringify(loginRes.body)}`);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const token = loginRes.body.token || loginRes.body.access_token || (loginRes.body.data && loginRes.body.data.token);
|
|
798
|
+
if (!token) die(`No token in login response: ${JSON.stringify(loginRes.body)}`);
|
|
799
|
+
|
|
800
|
+
// Decode entity concept ID from JWT upn claim
|
|
801
|
+
let entityId;
|
|
802
|
+
try {
|
|
803
|
+
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString());
|
|
804
|
+
entityId = payload.upn || payload.sub;
|
|
805
|
+
} catch {
|
|
806
|
+
die('Could not decode JWT token.');
|
|
807
|
+
}
|
|
808
|
+
if (!entityId) die('Could not determine entity ID from JWT.');
|
|
809
|
+
console.log(`done (entity ${entityId})`);
|
|
810
|
+
|
|
811
|
+
// Step 2: generate client-secret
|
|
812
|
+
process.stdout.write('[2/3] Generating client-secret… ');
|
|
813
|
+
let secretRes;
|
|
814
|
+
try {
|
|
815
|
+
secretRes = await httpPostWithToken(`${baseUrl}/client-secret`, token);
|
|
816
|
+
} catch (e) {
|
|
817
|
+
die(`Request to /api/client-secret failed: ${e.message}`);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
if (secretRes.status !== 200 || !secretRes.body) {
|
|
821
|
+
die(`client-secret failed (HTTP ${secretRes.status}): ${JSON.stringify(secretRes.body)}`);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const secret = secretRes.body.message || secretRes.body.data || secretRes.body;
|
|
825
|
+
if (!secret || typeof secret !== 'string') die(`Empty secret in response: ${JSON.stringify(secretRes.body)}`);
|
|
826
|
+
console.log('done');
|
|
827
|
+
|
|
828
|
+
// Step 3: update .env
|
|
829
|
+
process.stdout.write('[3/3] Updating .env… ');
|
|
830
|
+
setEnvVar(envPath, 'CLIENT_ID', String(entityId));
|
|
831
|
+
setEnvVar(envPath, 'CLIENT_SECRET', secret);
|
|
832
|
+
console.log('done');
|
|
833
|
+
|
|
834
|
+
console.log(`
|
|
835
|
+
─────────────────────────────────────────
|
|
836
|
+
Credentials saved to .env:
|
|
837
|
+
CLIENT_ID = ${entityId}
|
|
838
|
+
CLIENT_SECRET = ${secret}
|
|
839
|
+
─────────────────────────────────────────
|
|
840
|
+
|
|
841
|
+
Restart the node-server to apply:
|
|
842
|
+
freeschema restart node-server
|
|
843
|
+
`);
|
|
844
|
+
}
|
|
845
|
+
|
|
704
846
|
// ── help ──────────────────────────────────────────────────────────────────────
|
|
705
847
|
|
|
706
848
|
function cmdHelp() {
|
|
@@ -725,6 +867,8 @@ Commands:
|
|
|
725
867
|
update Update CLI + pull latest images and restart
|
|
726
868
|
down [-v] Remove containers (-v also removes data volumes)
|
|
727
869
|
|
|
870
|
+
setup-credentials Login as admin, generate CLIENT_SECRET, save to .env
|
|
871
|
+
|
|
728
872
|
db seed --url <url> Download SQL seed file into seed-db/
|
|
729
873
|
db seed --file <path> Copy local SQL file into seed-db/
|
|
730
874
|
db backup [file] Dump database to a .sql file
|
|
@@ -746,7 +890,7 @@ Examples:
|
|
|
746
890
|
|
|
747
891
|
switch (command) {
|
|
748
892
|
case 'init': cmdInit().catch(e => { console.error(e); process.exit(1); }); break;
|
|
749
|
-
case 'start': cmdStart();
|
|
893
|
+
case 'start': cmdStart().catch(e => { console.error(e); process.exit(1); }); break;
|
|
750
894
|
case 'stop': cmdStop(); break;
|
|
751
895
|
case 'restart': cmdRestart(); break;
|
|
752
896
|
case 'status': cmdStatus(); break;
|
|
@@ -754,7 +898,8 @@ switch (command) {
|
|
|
754
898
|
case 'pull': cmdPull(); break;
|
|
755
899
|
case 'update': cmdUpdate(); break;
|
|
756
900
|
case 'down': cmdDown(); break;
|
|
757
|
-
case 'db':
|
|
901
|
+
case 'db': cmdDb(); break;
|
|
902
|
+
case 'setup-credentials': cmdSetupCredentials().catch(e => { console.error(e); process.exit(1); }); break;
|
|
758
903
|
case '--version': case '-v': console.log(VERSION); break;
|
|
759
904
|
case '--help': case '-h': case undefined: cmdHelp(); break;
|
|
760
905
|
default:
|
package/package.json
CHANGED
package/templates/.env.example
CHANGED
|
@@ -99,9 +99,9 @@ LINKEDIN_SECRET=""
|
|
|
99
99
|
# ====================================
|
|
100
100
|
# PAYMENT GATEWAY (STRIPE)
|
|
101
101
|
# ====================================
|
|
102
|
-
STRIPE_SECRET=""
|
|
102
|
+
STRIPE_SECRET="sk_test_placeholder"
|
|
103
103
|
WEBHOOK_KEY=""
|
|
104
|
-
STRIPE_TEST_SECRET=""
|
|
104
|
+
STRIPE_TEST_SECRET="sk_test_placeholder"
|
|
105
105
|
WEBHOOK_TEST_KEY=""
|
|
106
106
|
|
|
107
107
|
# ====================================
|