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 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(); break;
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': cmdDb(); break;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freeschema",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "FreeSchema — self-hosted deployment CLI",
5
5
  "keywords": [
6
6
  "freeschema",
@@ -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
  # ====================================