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 +147 -3
- package/package.json +1 -1
- package/templates/.env.example +2 -2
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();
|
|
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':
|
|
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
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
|
# ====================================
|