freeschema 1.0.6 → 1.0.8

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
@@ -510,7 +510,7 @@ function waitForMySQL(container, user, pass, maxAttempts = 30) {
510
510
  return false;
511
511
  }
512
512
 
513
- function cmdStart() {
513
+ async function cmdStart() {
514
514
  patchComposeFile();
515
515
  const cwd = process.cwd();
516
516
  const missing = ['.env', '.env.frontend'].filter(f => !fs.existsSync(path.join(cwd, f)));
@@ -560,6 +560,31 @@ function cmdStart() {
560
560
  console.log(' Status: freeschema status');
561
561
  console.log(' Logs: freeschema logs');
562
562
  console.log(' Stop: freeschema stop');
563
+
564
+ // Auto-setup credentials if CLIENT_SECRET is missing or placeholder
565
+ const envAfterStart = readEnv();
566
+ const clientSecret = envAfterStart.CLIENT_SECRET || '';
567
+ const isPlaceholder = clientSecret === '' || clientSecret === 'sk_test_placeholder';
568
+ if (isPlaceholder) {
569
+ console.log('\n─────────────────────────────────────────');
570
+ console.log(' CLIENT_SECRET is not configured.');
571
+ console.log(' Setting up admin credentials…');
572
+ console.log('─────────────────────────────────────────');
573
+
574
+ // Wait for MySQL to be ready before inserting credentials
575
+ try {
576
+ const container = getMysqlContainer();
577
+ const env = readEnv();
578
+ const dbUser = env.DB_USER || env.MYSQL_USER || 'freeschema';
579
+ const dbPass = env.DB_PASSWORD || env.MYSQL_PASSWORD || 'Freeschema@123';
580
+ waitForMySQL(container, dbUser, dbPass);
581
+ } catch (e) {
582
+ console.log(' (skipping credentials setup — MySQL not running)');
583
+ return;
584
+ }
585
+
586
+ await cmdSetupCredentials();
587
+ }
563
588
  }
564
589
 
565
590
  // ── other commands ────────────────────────────────────────────────────────────
@@ -701,6 +726,122 @@ function cmdDbRestore() {
701
726
  console.log('Restore complete.');
702
727
  }
703
728
 
729
+ // ── setup-credentials ─────────────────────────────────────────────────────────
730
+
731
+ function hashClientSecret(plainSecret) {
732
+ // Matches C# ClientSecretHasher: PBKDF2-HMAC-SHA256, 100000 iterations, 16-byte salt, 32-byte output
733
+ // Stored format: "{iterations}.{base64_salt}.{base64_hash}"
734
+ const iterations = 100_000;
735
+ const salt = crypto.randomBytes(16);
736
+ const hash = crypto.pbkdf2Sync(plainSecret, salt, iterations, 32, 'sha256');
737
+ return `${iterations}.${salt.toString('base64')}.${hash.toString('base64')}`;
738
+ }
739
+
740
+ function runSql(container, dbName, sql) {
741
+ const env = readEnv();
742
+ const dbPass = env.MYSQL_ROOT_PASSWORD || ('Admin@' + (env.MYSQL_PASSWORD || 'Freeschema@123'));
743
+ const result = spawnSync(
744
+ 'docker',
745
+ ['exec', '-i', container, 'mysql', '-uroot', `-p${dbPass}`, dbName],
746
+ { input: sql, stdio: ['pipe', 'pipe', 'pipe'] }
747
+ );
748
+ if (result.error) throw new Error(`docker exec failed: ${result.error.message}`);
749
+ if (result.status !== 0) throw new Error((result.stderr || '').toString().trim());
750
+ return (result.stdout || '').toString().trim();
751
+ }
752
+
753
+ async function cmdSetupCredentials() {
754
+ const envPath = path.join(process.cwd(), '.env');
755
+ if (!fs.existsSync(envPath)) die('.env not found. Run `freeschema init` first.');
756
+
757
+ const env = readEnv();
758
+ const dbName = env.DB_DATABASE || env.MYSQL_DATABASE || 'freeschema_db';
759
+
760
+ console.log('\nSetting up CLIENT_ID and CLIENT_SECRET');
761
+
762
+ // Prompt for entity concept ID
763
+ const entityId = await prompt('Admin entity concept ID', env.CLIENT_ID || '100000016');
764
+ if (!entityId) die('Entity ID is required.');
765
+
766
+ // Step 1: generate secret + hash it locally — no API call needed
767
+ process.stdout.write('[1/3] Generating secret… ');
768
+ const plainSecret = crypto.randomBytes(32).toString('hex'); // 64-char hex
769
+ const hashedSecret = hashClientSecret(plainSecret);
770
+ console.log('done');
771
+
772
+ // Step 2: insert hash directly into the database
773
+ process.stdout.write('[2/3] Writing to database… ');
774
+ const container = getMysqlContainer();
775
+
776
+ const sql = `
777
+ SET @entity_id = ${parseInt(entityId, 10)};
778
+
779
+ -- Look up the type concept for 'the_client_secret'
780
+ SET @type_id = (
781
+ SELECT id FROM the_concepts
782
+ WHERE character_value = 'the_client_secret'
783
+ ORDER BY id ASC LIMIT 1
784
+ );
785
+
786
+ -- Insert the secret hash as a new concept
787
+ INSERT INTO the_concepts (
788
+ user_id, category_id, category_user_id, type_id, type_user_id,
789
+ character_value, security_id, security_user_id, access_id, access_user_id,
790
+ session_information_id, session_information_user_id
791
+ ) VALUES (
792
+ 999, @type_id, 999, @type_id, 999,
793
+ '${hashedSecret}', 4, 999, 4, 999, 999, 999
794
+ );
795
+
796
+ SET @secret_concept_id = LAST_INSERT_ID();
797
+
798
+ -- Look up the connection type for 'the_entity_s_client_secret'
799
+ SET @conn_type_id = (
800
+ SELECT id FROM the_concepts
801
+ WHERE character_value = 'the_entity_s_client_secret'
802
+ ORDER BY id ASC LIMIT 1
803
+ );
804
+
805
+ -- Link entity → secret concept
806
+ INSERT INTO the_connections (
807
+ user_id, of_the_concepts_id, of_the_concepts_user_id,
808
+ type_id, type_user_id, order_id, order_user_id,
809
+ to_the_concepts_id, to_the_concepts_user_id,
810
+ security_id, security_user_id, access_id, access_user_id,
811
+ session_information_id, session_information_user_id
812
+ ) VALUES (
813
+ 999, @entity_id, 999,
814
+ @conn_type_id, 999, 999, 999,
815
+ @secret_concept_id, 999,
816
+ 4, 999, 4, 999, 999, 999
817
+ );
818
+ `;
819
+
820
+ try {
821
+ runSql(container, dbName, sql);
822
+ } catch (e) {
823
+ die(`Database insert failed: ${e.message}`);
824
+ }
825
+ console.log('done');
826
+
827
+ // Step 3: update .env
828
+ process.stdout.write('[3/3] Updating .env… ');
829
+ setEnvVar(envPath, 'CLIENT_ID', String(entityId));
830
+ setEnvVar(envPath, 'CLIENT_SECRET', plainSecret);
831
+ console.log('done');
832
+
833
+ console.log(`
834
+ ─────────────────────────────────────────
835
+ Credentials saved to .env:
836
+ CLIENT_ID = ${entityId}
837
+ CLIENT_SECRET = ${plainSecret}
838
+ ─────────────────────────────────────────
839
+
840
+ Restart the node-server to apply:
841
+ freeschema restart node-server
842
+ `);
843
+ }
844
+
704
845
  // ── help ──────────────────────────────────────────────────────────────────────
705
846
 
706
847
  function cmdHelp() {
@@ -725,6 +866,8 @@ Commands:
725
866
  update Update CLI + pull latest images and restart
726
867
  down [-v] Remove containers (-v also removes data volumes)
727
868
 
869
+ setup-credentials Login as admin, generate CLIENT_SECRET, save to .env
870
+
728
871
  db seed --url <url> Download SQL seed file into seed-db/
729
872
  db seed --file <path> Copy local SQL file into seed-db/
730
873
  db backup [file] Dump database to a .sql file
@@ -746,7 +889,7 @@ Examples:
746
889
 
747
890
  switch (command) {
748
891
  case 'init': cmdInit().catch(e => { console.error(e); process.exit(1); }); break;
749
- case 'start': cmdStart(); break;
892
+ case 'start': cmdStart().catch(e => { console.error(e); process.exit(1); }); break;
750
893
  case 'stop': cmdStop(); break;
751
894
  case 'restart': cmdRestart(); break;
752
895
  case 'status': cmdStatus(); break;
@@ -754,7 +897,8 @@ switch (command) {
754
897
  case 'pull': cmdPull(); break;
755
898
  case 'update': cmdUpdate(); break;
756
899
  case 'down': cmdDown(); break;
757
- case 'db': cmdDb(); break;
900
+ case 'db': cmdDb(); break;
901
+ case 'setup-credentials': cmdSetupCredentials().catch(e => { console.error(e); process.exit(1); }); break;
758
902
  case '--version': case '-v': console.log(VERSION); break;
759
903
  case '--help': case '-h': case undefined: cmdHelp(); break;
760
904
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freeschema",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
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
  # ====================================