freeschema 1.0.8 → 1.0.10
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 +132 -95
- package/package.json +1 -1
- package/templates/.env.example +5 -0
- package/templates/docker-compose.yml +32 -7
package/bin/freeschema.js
CHANGED
|
@@ -122,6 +122,47 @@ function generateMosquittoPassword(username, password) {
|
|
|
122
122
|
return `${username}:$7$${iterations}$${salt.toString('base64')}$${hash.toString('base64')}\n`;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
function waitForAccessControl(container) {
|
|
126
|
+
process.stdout.write(' Waiting for access-control-server');
|
|
127
|
+
for (let i = 0; i < 30; i++) {
|
|
128
|
+
const r = spawnSync('docker', [
|
|
129
|
+
'exec', container, 'curl', '-sf', 'http://localhost:7000/health'
|
|
130
|
+
], { stdio: 'pipe' });
|
|
131
|
+
if (r.status === 0) { process.stdout.write(' ready\n'); return true; }
|
|
132
|
+
process.stdout.write('.');
|
|
133
|
+
sleep(2000);
|
|
134
|
+
}
|
|
135
|
+
process.stdout.write(' timed out\n');
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function setupSuperAdmin() {
|
|
140
|
+
const env = readEnv();
|
|
141
|
+
const adminId = parseInt(env.SUPER_ADMIN_ID || '100000016', 10);
|
|
142
|
+
const out = runComposeOutput(['ps', '--format', 'json']);
|
|
143
|
+
const containers = out.split('\n')
|
|
144
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
145
|
+
.filter(Boolean);
|
|
146
|
+
const ac = containers.find(c => c.Service === 'access-control-server');
|
|
147
|
+
if (!ac) return;
|
|
148
|
+
const container = ac.Name || ac.ID;
|
|
149
|
+
|
|
150
|
+
if (!waitForAccessControl(container)) return;
|
|
151
|
+
|
|
152
|
+
const result = spawnSync('docker', [
|
|
153
|
+
'exec', container, 'curl', '-sf',
|
|
154
|
+
'-X', 'POST', 'http://localhost:7000/api/access/super-admin/concept',
|
|
155
|
+
'-H', 'Content-Type: application/json',
|
|
156
|
+
'-d', JSON.stringify({ ConceptId: adminId })
|
|
157
|
+
], { stdio: ['inherit', 'pipe', 'pipe'] });
|
|
158
|
+
|
|
159
|
+
if (result.status === 0) {
|
|
160
|
+
console.log(` Super admin concept ${adminId} ✓`);
|
|
161
|
+
} else {
|
|
162
|
+
console.log(` Super admin already set or skipped`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
125
166
|
function ensureMosquittoConfig(cwd) {
|
|
126
167
|
const dir = (sub) => path.join(cwd, 'mosquitto', sub);
|
|
127
168
|
['config', 'data', 'log'].forEach(d => {
|
|
@@ -556,6 +597,9 @@ async function cmdStart() {
|
|
|
556
597
|
console.log(' Seed applied ✓');
|
|
557
598
|
}
|
|
558
599
|
|
|
600
|
+
console.log('\nFinalizing setup…');
|
|
601
|
+
setupSuperAdmin();
|
|
602
|
+
|
|
559
603
|
console.log('\nFreeSchema is running.');
|
|
560
604
|
console.log(' Status: freeschema status');
|
|
561
605
|
console.log(' Logs: freeschema logs');
|
|
@@ -570,19 +614,6 @@ async function cmdStart() {
|
|
|
570
614
|
console.log(' CLIENT_SECRET is not configured.');
|
|
571
615
|
console.log(' Setting up admin credentials…');
|
|
572
616
|
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
617
|
await cmdSetupCredentials();
|
|
587
618
|
}
|
|
588
619
|
}
|
|
@@ -728,116 +759,122 @@ function cmdDbRestore() {
|
|
|
728
759
|
|
|
729
760
|
// ── setup-credentials ─────────────────────────────────────────────────────────
|
|
730
761
|
|
|
731
|
-
function
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
762
|
+
function getWicoContainer() {
|
|
763
|
+
const out = runComposeOutput(['ps', '--format', 'json']);
|
|
764
|
+
if (!out) return null;
|
|
765
|
+
const containers = out.split('\n')
|
|
766
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
767
|
+
.filter(Boolean);
|
|
768
|
+
const wico = containers.find(c => c.Service === 'wico-server' || (c.Name || '').includes('wico-server'));
|
|
769
|
+
return wico ? (wico.Name || wico.ID) : null;
|
|
738
770
|
}
|
|
739
771
|
|
|
740
|
-
function
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
772
|
+
function waitForWicoServer(container, maxAttempts = 60) {
|
|
773
|
+
process.stdout.write(' Waiting for wico-server');
|
|
774
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
775
|
+
const r = spawnSync('docker', [
|
|
776
|
+
'exec', container, 'curl', '-sf', 'http://localhost:7000/health'
|
|
777
|
+
], { stdio: 'pipe' });
|
|
778
|
+
if (r.status === 0) { process.stdout.write(' ready\n'); return true; }
|
|
779
|
+
process.stdout.write('.');
|
|
780
|
+
sleep(3000);
|
|
781
|
+
}
|
|
782
|
+
process.stdout.write(' timed out\n');
|
|
783
|
+
return false;
|
|
751
784
|
}
|
|
752
785
|
|
|
753
786
|
async function cmdSetupCredentials() {
|
|
754
787
|
const envPath = path.join(process.cwd(), '.env');
|
|
755
788
|
if (!fs.existsSync(envPath)) die('.env not found. Run `freeschema init` first.');
|
|
756
789
|
|
|
757
|
-
|
|
758
|
-
const dbName = env.DB_DATABASE || env.MYSQL_DATABASE || 'freeschema_db';
|
|
790
|
+
console.log('\nSetting up CLIENT_ID and CLIENT_SECRET via API\n');
|
|
759
791
|
|
|
760
|
-
|
|
792
|
+
// Find wico-server container
|
|
793
|
+
const container = getWicoContainer();
|
|
794
|
+
if (!container) die('wico-server is not running.\nStart with `freeschema start`.');
|
|
761
795
|
|
|
762
|
-
//
|
|
763
|
-
|
|
764
|
-
|
|
796
|
+
// Wait for wico-server to be healthy
|
|
797
|
+
if (!waitForWicoServer(container)) {
|
|
798
|
+
die('wico-server did not become ready.\nCheck logs: freeschema logs wico-server');
|
|
799
|
+
}
|
|
765
800
|
|
|
766
|
-
//
|
|
767
|
-
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
801
|
+
// Prompt for admin credentials
|
|
802
|
+
const email = await prompt('Admin email');
|
|
803
|
+
const password = await promptSecret('Admin password');
|
|
804
|
+
if (!email || !password) die('Email and password are required.');
|
|
805
|
+
|
|
806
|
+
// Step 1: Login
|
|
807
|
+
process.stdout.write('\n[1/3] Logging in… ');
|
|
808
|
+
const loginBody = JSON.stringify({ email, password, application: 'boomconsole' });
|
|
809
|
+
const loginResult = spawnSync('docker', [
|
|
810
|
+
'exec', container, 'curl', '-sf',
|
|
811
|
+
'-X', 'POST', 'http://localhost:7000/api/auth/login',
|
|
812
|
+
'-H', 'Content-Type: application/json',
|
|
813
|
+
'-d', loginBody
|
|
814
|
+
], { stdio: ['inherit', 'pipe', 'pipe'] });
|
|
815
|
+
|
|
816
|
+
if (loginResult.status !== 0) {
|
|
817
|
+
const errText = (loginResult.stderr || '').toString().trim() || 'curl returned non-zero';
|
|
818
|
+
die(`Login request failed: ${errText}`);
|
|
819
|
+
}
|
|
771
820
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
821
|
+
let loginRes;
|
|
822
|
+
try {
|
|
823
|
+
loginRes = JSON.parse((loginResult.stdout || '').toString().trim());
|
|
824
|
+
} catch {
|
|
825
|
+
die('Invalid JSON in login response — is wico-server healthy?');
|
|
826
|
+
}
|
|
775
827
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
);
|
|
795
|
-
|
|
796
|
-
|
|
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
|
-
`;
|
|
828
|
+
// Response shape: { status: 200, data: { token, userConcept, ... } }
|
|
829
|
+
const token = loginRes?.data?.token;
|
|
830
|
+
const entityId = loginRes?.data?.userConcept;
|
|
831
|
+
if (!token) {
|
|
832
|
+
die(`Login failed: ${JSON.stringify(loginRes)}`);
|
|
833
|
+
}
|
|
834
|
+
console.log(`done (entity ${entityId})`);
|
|
835
|
+
|
|
836
|
+
// Step 2: Generate client-secret
|
|
837
|
+
process.stdout.write('[2/3] Generating client-secret… ');
|
|
838
|
+
const secretResult = spawnSync('docker', [
|
|
839
|
+
'exec', container, 'curl', '-sf',
|
|
840
|
+
'-X', 'POST', 'http://localhost:7000/api/client-secret',
|
|
841
|
+
'-H', `Authorization: Bearer ${token}`,
|
|
842
|
+
'-H', 'Content-Type: application/json'
|
|
843
|
+
], { stdio: ['inherit', 'pipe', 'pipe'] });
|
|
844
|
+
|
|
845
|
+
if (secretResult.status !== 0) {
|
|
846
|
+
const errText = (secretResult.stderr || '').toString().trim() || 'curl returned non-zero';
|
|
847
|
+
die(`/api/client-secret request failed: ${errText}`);
|
|
848
|
+
}
|
|
819
849
|
|
|
850
|
+
let secretRes;
|
|
820
851
|
try {
|
|
821
|
-
|
|
822
|
-
} catch
|
|
823
|
-
die(
|
|
852
|
+
secretRes = JSON.parse((secretResult.stdout || '').toString().trim());
|
|
853
|
+
} catch {
|
|
854
|
+
die('Invalid JSON in client-secret response');
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Response shape: { success: true, message: "<plainSecretString>" }
|
|
858
|
+
const secret = secretRes?.message;
|
|
859
|
+
if (!secret || typeof secret !== 'string' || !secretRes?.success) {
|
|
860
|
+
die(`Unexpected client-secret response: ${JSON.stringify(secretRes)}`);
|
|
824
861
|
}
|
|
825
862
|
console.log('done');
|
|
826
863
|
|
|
827
|
-
// Step 3:
|
|
864
|
+
// Step 3: Update .env
|
|
828
865
|
process.stdout.write('[3/3] Updating .env… ');
|
|
829
866
|
setEnvVar(envPath, 'CLIENT_ID', String(entityId));
|
|
830
|
-
setEnvVar(envPath, 'CLIENT_SECRET',
|
|
867
|
+
setEnvVar(envPath, 'CLIENT_SECRET', secret);
|
|
831
868
|
console.log('done');
|
|
832
869
|
|
|
833
870
|
console.log(`
|
|
834
871
|
─────────────────────────────────────────
|
|
835
872
|
Credentials saved to .env:
|
|
836
873
|
CLIENT_ID = ${entityId}
|
|
837
|
-
CLIENT_SECRET =
|
|
874
|
+
CLIENT_SECRET = (saved — restart to apply)
|
|
838
875
|
─────────────────────────────────────────
|
|
839
876
|
|
|
840
|
-
Restart
|
|
877
|
+
Restart node-server and node-cache-server to pick up the new secret:
|
|
841
878
|
freeschema restart node-server
|
|
842
879
|
`);
|
|
843
880
|
}
|
package/package.json
CHANGED
package/templates/.env.example
CHANGED
|
@@ -188,6 +188,12 @@ services:
|
|
|
188
188
|
condition: service_started
|
|
189
189
|
networks:
|
|
190
190
|
- freeschema-network
|
|
191
|
+
healthcheck:
|
|
192
|
+
test: ["CMD-SHELL", "curl -sI http://localhost:5000/health || exit 1"]
|
|
193
|
+
interval: 1m30s
|
|
194
|
+
timeout: 30s
|
|
195
|
+
retries: 5
|
|
196
|
+
start_period: 30s
|
|
191
197
|
|
|
192
198
|
log-server:
|
|
193
199
|
image: mentorayush/log-server:latest
|
|
@@ -229,6 +235,12 @@ services:
|
|
|
229
235
|
- node-server
|
|
230
236
|
networks:
|
|
231
237
|
- freeschema-network
|
|
238
|
+
healthcheck:
|
|
239
|
+
test: ["CMD-SHELL", "wget -q --spider http://localhost/ || exit 1"]
|
|
240
|
+
interval: 30s
|
|
241
|
+
timeout: 10s
|
|
242
|
+
retries: 3
|
|
243
|
+
start_period: 30s
|
|
232
244
|
|
|
233
245
|
wico-app:
|
|
234
246
|
image: mentorayush/wico-app:latest
|
|
@@ -243,6 +255,12 @@ services:
|
|
|
243
255
|
- node-server
|
|
244
256
|
networks:
|
|
245
257
|
- freeschema-network
|
|
258
|
+
healthcheck:
|
|
259
|
+
test: ["CMD-SHELL", "wget -q --spider http://localhost/ || exit 1"]
|
|
260
|
+
interval: 30s
|
|
261
|
+
timeout: 10s
|
|
262
|
+
retries: 3
|
|
263
|
+
start_period: 30s
|
|
246
264
|
|
|
247
265
|
nginx-server:
|
|
248
266
|
image: nginx:alpine
|
|
@@ -255,13 +273,20 @@ services:
|
|
|
255
273
|
volumes:
|
|
256
274
|
- ./nginx/nginx.conf.template:/etc/nginx/templates/default.conf.template:ro
|
|
257
275
|
depends_on:
|
|
258
|
-
-
|
|
259
|
-
|
|
260
|
-
-
|
|
261
|
-
|
|
262
|
-
-
|
|
263
|
-
|
|
264
|
-
-
|
|
276
|
+
access-control-server:
|
|
277
|
+
condition: service_healthy
|
|
278
|
+
wico-server:
|
|
279
|
+
condition: service_healthy
|
|
280
|
+
node-server:
|
|
281
|
+
condition: service_healthy
|
|
282
|
+
node-cache-server:
|
|
283
|
+
condition: service_healthy
|
|
284
|
+
log-server:
|
|
285
|
+
condition: service_healthy
|
|
286
|
+
vccs-app:
|
|
287
|
+
condition: service_healthy
|
|
288
|
+
wico-app:
|
|
289
|
+
condition: service_healthy
|
|
265
290
|
networks:
|
|
266
291
|
- freeschema-network
|
|
267
292
|
|