@edge-base/cli 0.1.5 → 0.2.0
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/README.md +2 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +73 -52
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +15 -5
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +81 -5
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/docker.d.ts.map +1 -1
- package/dist/commands/docker.js +153 -42
- package/dist/commands/docker.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +72 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/lib/admin-bootstrap.d.ts +61 -0
- package/dist/lib/admin-bootstrap.d.ts.map +1 -0
- package/dist/lib/admin-bootstrap.js +287 -0
- package/dist/lib/admin-bootstrap.js.map +1 -0
- package/dist/lib/dev-sidecar.d.ts.map +1 -1
- package/dist/lib/dev-sidecar.js +142 -45
- package/dist/lib/dev-sidecar.js.map +1 -1
- package/dist/lib/load-config.d.ts.map +1 -1
- package/dist/lib/load-config.js +30 -18
- package/dist/lib/load-config.js.map +1 -1
- package/dist/lib/pnpm.d.ts +8 -0
- package/dist/lib/pnpm.d.ts.map +1 -0
- package/dist/lib/pnpm.js +10 -0
- package/dist/lib/pnpm.js.map +1 -0
- package/llms.txt +1 -0
- package/package.json +3 -3
package/dist/lib/dev-sidecar.js
CHANGED
|
@@ -41,8 +41,7 @@ async function verifyAdminJwt(token, secret) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
async function verifyAuth(req, adminSecret) {
|
|
44
|
-
const internalSecret = req.headers['x-edgebase-internal-secret'] ??
|
|
45
|
-
req.headers['X-EdgeBase-Internal-Secret'];
|
|
44
|
+
const internalSecret = req.headers['x-edgebase-internal-secret'] ?? req.headers['X-EdgeBase-Internal-Secret'];
|
|
46
45
|
const internalValue = Array.isArray(internalSecret) ? internalSecret[0] : internalSecret;
|
|
47
46
|
if (internalValue && internalValue === adminSecret) {
|
|
48
47
|
return true;
|
|
@@ -81,6 +80,14 @@ function readBody(req) {
|
|
|
81
80
|
function parsePath(urlPath) {
|
|
82
81
|
return urlPath.split('/').filter(Boolean);
|
|
83
82
|
}
|
|
83
|
+
function sanitizeForLog(value) {
|
|
84
|
+
const escape = String.fromCharCode(27);
|
|
85
|
+
const ansiPattern = new RegExp(`${escape}\\[[0-9;]*[A-Za-z]`, 'g');
|
|
86
|
+
const cleaned = value
|
|
87
|
+
.replace(ansiPattern, '')
|
|
88
|
+
.replace(/[\r\n\t]+/g, ' ');
|
|
89
|
+
return cleaned.replace(/[^ -~]/g, '?');
|
|
90
|
+
}
|
|
84
91
|
function isDynamicDbBlock(dbBlock) {
|
|
85
92
|
if (!dbBlock)
|
|
86
93
|
return false;
|
|
@@ -96,8 +103,8 @@ function parseAuthSettingsTarget(value) {
|
|
|
96
103
|
return value === 'release' ? 'release' : 'development';
|
|
97
104
|
}
|
|
98
105
|
function isManagedAuthEnvKey(key) {
|
|
99
|
-
return MANAGED_AUTH_ENV_KEYS.includes(key)
|
|
100
|
-
|
|
106
|
+
return (MANAGED_AUTH_ENV_KEYS.includes(key) ||
|
|
107
|
+
MANAGED_AUTH_ENV_PREFIXES.some((prefix) => key.startsWith(prefix)));
|
|
101
108
|
}
|
|
102
109
|
function readAuthEnvValues(projectDir, target) {
|
|
103
110
|
if (target === 'release') {
|
|
@@ -150,10 +157,12 @@ function getOAuthEnvKeys(provider) {
|
|
|
150
157
|
}
|
|
151
158
|
function getEnvTargets(projectDir, target) {
|
|
152
159
|
if (target === 'release') {
|
|
153
|
-
return [
|
|
160
|
+
return [
|
|
161
|
+
{
|
|
154
162
|
filePath: join(projectDir, '.env.release'),
|
|
155
163
|
header: RELEASE_ENV_HEADER,
|
|
156
|
-
}
|
|
164
|
+
},
|
|
165
|
+
];
|
|
157
166
|
}
|
|
158
167
|
const envDevelopmentPath = join(projectDir, '.env.development');
|
|
159
168
|
const devVarsPath = join(projectDir, '.dev.vars');
|
|
@@ -195,7 +204,10 @@ function syncOAuthSecretsToLocalEnv(projectDir, target, allowedOAuthProviders, o
|
|
|
195
204
|
}
|
|
196
205
|
if (envKeys.scopes) {
|
|
197
206
|
const scopesValue = Array.isArray(config.scopes)
|
|
198
|
-
? config.scopes
|
|
207
|
+
? config.scopes
|
|
208
|
+
.map((scope) => scope.trim())
|
|
209
|
+
.filter(Boolean)
|
|
210
|
+
.join(',')
|
|
199
211
|
: null;
|
|
200
212
|
updateEnvValue(projectDir, target, envKeys.scopes, scopesValue && scopesValue.length > 0 ? scopesValue : null);
|
|
201
213
|
}
|
|
@@ -231,10 +243,14 @@ function readAuthSettings(config) {
|
|
|
231
243
|
};
|
|
232
244
|
}
|
|
233
245
|
return {
|
|
234
|
-
providers: Array.isArray(authConfig.allowedOAuthProviders)
|
|
246
|
+
providers: Array.isArray(authConfig.allowedOAuthProviders)
|
|
247
|
+
? authConfig.allowedOAuthProviders
|
|
248
|
+
: [],
|
|
235
249
|
emailAuth: authConfig.emailAuth !== false,
|
|
236
250
|
anonymousAuth: !!authConfig.anonymousAuth,
|
|
237
|
-
allowedRedirectUrls: Array.isArray(authConfig.allowedRedirectUrls)
|
|
251
|
+
allowedRedirectUrls: Array.isArray(authConfig.allowedRedirectUrls)
|
|
252
|
+
? authConfig.allowedRedirectUrls
|
|
253
|
+
: [],
|
|
238
254
|
session: {
|
|
239
255
|
accessTokenTTL: authConfig.session?.accessTokenTTL ?? null,
|
|
240
256
|
refreshTokenTTL: authConfig.session?.refreshTokenTTL ?? null,
|
|
@@ -348,7 +364,7 @@ async function executeWorkerSql(opts, authorization, namespace, sql, params = []
|
|
|
348
364
|
}
|
|
349
365
|
let message = `SQL execution failed with ${response.status}`;
|
|
350
366
|
try {
|
|
351
|
-
const payload = await response.json();
|
|
367
|
+
const payload = (await response.json());
|
|
352
368
|
if (payload.message) {
|
|
353
369
|
message = payload.message;
|
|
354
370
|
}
|
|
@@ -370,7 +386,7 @@ async function callWorkerAdmin(opts, authorization, path, body) {
|
|
|
370
386
|
if (!response.ok) {
|
|
371
387
|
let message = `${path} failed with ${response.status}`;
|
|
372
388
|
try {
|
|
373
|
-
const payload = await response.json();
|
|
389
|
+
const payload = (await response.json());
|
|
374
390
|
if (payload.message) {
|
|
375
391
|
message = payload.message;
|
|
376
392
|
}
|
|
@@ -449,9 +465,11 @@ function readCurrentSchema(configPath) {
|
|
|
449
465
|
try {
|
|
450
466
|
// Use a quick approach: read config via tsx eval
|
|
451
467
|
const projectDir = resolve(configPath, '..');
|
|
468
|
+
const moduleUrl = pathToFileURL(resolve(configPath)).href;
|
|
452
469
|
const result = execTsxSync([
|
|
453
470
|
'-e',
|
|
454
|
-
`
|
|
471
|
+
`(async()=>{const moduleUrl=process.argv[1];const mod=await import(moduleUrl);const d=mod.default??mod;const s={};for(const [ns,b] of Object.entries(d.databases??{})){for(const [t,tc] of Object.entries((b as any).tables??{})){s[t]={namespace:ns,fields:(tc as any).schema??{},fts:(tc as any).fts??[]};}}console.log(JSON.stringify(s));})().catch((error)=>{console.error(error instanceof Error?error.message:String(error));process.exit(1);});`,
|
|
472
|
+
moduleUrl,
|
|
455
473
|
], { cwd: projectDir, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
456
474
|
return JSON.parse(result);
|
|
457
475
|
}
|
|
@@ -471,7 +489,10 @@ async function handleRoute(req, res, opts) {
|
|
|
471
489
|
return;
|
|
472
490
|
}
|
|
473
491
|
// POST /postgres/query — pooled local PostgreSQL query bridge for Worker dev mode
|
|
474
|
-
if (method === 'POST' &&
|
|
492
|
+
if (method === 'POST' &&
|
|
493
|
+
segments[0] === 'postgres' &&
|
|
494
|
+
segments[1] === 'query' &&
|
|
495
|
+
segments.length === 2) {
|
|
475
496
|
const body = await readBody(req);
|
|
476
497
|
const namespace = body.namespace;
|
|
477
498
|
const sql = body.sql;
|
|
@@ -492,7 +513,10 @@ async function handleRoute(req, res, opts) {
|
|
|
492
513
|
return;
|
|
493
514
|
}
|
|
494
515
|
// POST /postgres/ensure-schema — persistent local PostgreSQL schema warmup/cache
|
|
495
|
-
if (method === 'POST' &&
|
|
516
|
+
if (method === 'POST' &&
|
|
517
|
+
segments[0] === 'postgres' &&
|
|
518
|
+
segments[1] === 'ensure-schema' &&
|
|
519
|
+
segments.length === 2) {
|
|
496
520
|
const body = await readBody(req);
|
|
497
521
|
const namespace = body.namespace;
|
|
498
522
|
if (!namespace)
|
|
@@ -508,7 +532,10 @@ async function handleRoute(req, res, opts) {
|
|
|
508
532
|
return;
|
|
509
533
|
}
|
|
510
534
|
// POST /schema/tables — create table
|
|
511
|
-
if (method === 'POST' &&
|
|
535
|
+
if (method === 'POST' &&
|
|
536
|
+
segments[0] === 'schema' &&
|
|
537
|
+
segments[1] === 'tables' &&
|
|
538
|
+
segments.length === 2) {
|
|
512
539
|
const body = await readBody(req);
|
|
513
540
|
const dbKey = body.dbKey || 'shared';
|
|
514
541
|
const name = body.name;
|
|
@@ -520,7 +547,10 @@ async function handleRoute(req, res, opts) {
|
|
|
520
547
|
return;
|
|
521
548
|
}
|
|
522
549
|
// POST /schema/databases — create database block
|
|
523
|
-
if (method === 'POST' &&
|
|
550
|
+
if (method === 'POST' &&
|
|
551
|
+
segments[0] === 'schema' &&
|
|
552
|
+
segments[1] === 'databases' &&
|
|
553
|
+
segments.length === 2) {
|
|
524
554
|
const body = await readBody(req);
|
|
525
555
|
const name = body.name;
|
|
526
556
|
const topology = body.topology ?? 'single';
|
|
@@ -543,14 +573,18 @@ async function handleRoute(req, res, opts) {
|
|
|
543
573
|
return;
|
|
544
574
|
}
|
|
545
575
|
// POST /integrations/neon/databases — create postgres DB block + configure Neon envs
|
|
546
|
-
if (method === 'POST' &&
|
|
576
|
+
if (method === 'POST' &&
|
|
577
|
+
segments[0] === 'integrations' &&
|
|
578
|
+
segments[1] === 'neon' &&
|
|
579
|
+
segments[2] === 'databases' &&
|
|
580
|
+
segments.length === 3) {
|
|
547
581
|
const body = await readBody(req);
|
|
548
582
|
const name = body.name;
|
|
549
583
|
const topology = body.topology ?? 'single';
|
|
550
584
|
const envKey = body.envKey;
|
|
551
585
|
const projectName = body.projectName;
|
|
552
586
|
const projectId = body.projectId;
|
|
553
|
-
const mode =
|
|
587
|
+
const mode = body.mode ?? 'reuse';
|
|
554
588
|
const targetLabel = body.targetLabel;
|
|
555
589
|
const placeholder = body.placeholder;
|
|
556
590
|
const helperText = body.helperText;
|
|
@@ -588,12 +622,16 @@ async function handleRoute(req, res, opts) {
|
|
|
588
622
|
return;
|
|
589
623
|
}
|
|
590
624
|
// POST /integrations/neon/connect — configure Neon envs for an existing postgres DB block
|
|
591
|
-
if (method === 'POST' &&
|
|
625
|
+
if (method === 'POST' &&
|
|
626
|
+
segments[0] === 'integrations' &&
|
|
627
|
+
segments[1] === 'neon' &&
|
|
628
|
+
segments[2] === 'connect' &&
|
|
629
|
+
segments.length === 3) {
|
|
592
630
|
const body = await readBody(req);
|
|
593
631
|
const namespace = body.namespace;
|
|
594
632
|
const envKey = body.envKey;
|
|
595
633
|
const projectId = body.projectId;
|
|
596
|
-
const mode =
|
|
634
|
+
const mode = body.mode ?? 'reuse';
|
|
597
635
|
if (!namespace)
|
|
598
636
|
throw new Error('namespace is required.');
|
|
599
637
|
const config = loadSidecarConfig(opts);
|
|
@@ -628,13 +666,17 @@ async function handleRoute(req, res, opts) {
|
|
|
628
666
|
return;
|
|
629
667
|
}
|
|
630
668
|
// POST /integrations/neon/upgrade — migrate a D1 single DB block to Neon-backed postgres
|
|
631
|
-
if (method === 'POST' &&
|
|
669
|
+
if (method === 'POST' &&
|
|
670
|
+
segments[0] === 'integrations' &&
|
|
671
|
+
segments[1] === 'neon' &&
|
|
672
|
+
segments[2] === 'upgrade' &&
|
|
673
|
+
segments.length === 3) {
|
|
632
674
|
const body = await readBody(req);
|
|
633
675
|
const namespace = body.namespace;
|
|
634
676
|
const envKey = body.envKey;
|
|
635
677
|
const projectName = body.projectName;
|
|
636
678
|
const projectId = body.projectId;
|
|
637
|
-
const mode =
|
|
679
|
+
const mode = body.mode ?? 'reuse';
|
|
638
680
|
const authorization = req.headers.authorization;
|
|
639
681
|
if (!namespace)
|
|
640
682
|
throw new Error('namespace is required.');
|
|
@@ -646,8 +688,9 @@ async function handleRoute(req, res, opts) {
|
|
|
646
688
|
throw new Error(`Only D1-backed single database blocks can be upgraded automatically. '${namespace}' is on '${provider}'.`);
|
|
647
689
|
}
|
|
648
690
|
const effectiveEnvKey = resolveRequestedPostgresEnvKey(namespace, envKey);
|
|
691
|
+
const namespaceLabel = sanitizeForLog(namespace);
|
|
649
692
|
console.log();
|
|
650
|
-
console.log(chalk.blue(`📦 Starting D1 -> Postgres migration for database block '${
|
|
693
|
+
console.log(chalk.blue(`📦 Starting D1 -> Postgres migration for database block '${namespaceLabel}'...`));
|
|
651
694
|
console.log(chalk.dim(' This migrates every table in the block, not just the current table.'));
|
|
652
695
|
console.log(chalk.dim(' 1/4 Exporting all tables from D1...'));
|
|
653
696
|
const dump = await callWorkerAdmin(opts, authorization, 'data/backup/dump-data', { namespace });
|
|
@@ -675,7 +718,7 @@ async function handleRoute(req, res, opts) {
|
|
|
675
718
|
tables: dump.tables,
|
|
676
719
|
skipWipe: false,
|
|
677
720
|
});
|
|
678
|
-
console.log(chalk.green(`✓ Database block '${
|
|
721
|
+
console.log(chalk.green(`✓ Database block '${namespaceLabel}' is now running on Postgres (${dumpedTableCount} table${dumpedTableCount === 1 ? '' : 's'} restored).`));
|
|
679
722
|
json(res, 200, {
|
|
680
723
|
ok: true,
|
|
681
724
|
mode,
|
|
@@ -687,12 +730,16 @@ async function handleRoute(req, res, opts) {
|
|
|
687
730
|
return;
|
|
688
731
|
}
|
|
689
732
|
// GET /integrations/neon/projects — list existing Neon projects for dashboard selection
|
|
690
|
-
if (method === 'GET' &&
|
|
733
|
+
if (method === 'GET' &&
|
|
734
|
+
segments[0] === 'integrations' &&
|
|
735
|
+
segments[1] === 'neon' &&
|
|
736
|
+
segments[2] === 'projects' &&
|
|
737
|
+
segments.length === 3) {
|
|
691
738
|
const forceRefresh = url.searchParams.get('refresh') === '1';
|
|
692
|
-
const isCacheFresh = !forceRefresh
|
|
693
|
-
&&
|
|
694
|
-
|
|
695
|
-
const items =
|
|
739
|
+
const isCacheFresh = !forceRefresh &&
|
|
740
|
+
neonProjectsCache &&
|
|
741
|
+
Date.now() - neonProjectsCache.loadedAt < NEON_PROJECT_CACHE_TTL_MS;
|
|
742
|
+
const items = isCacheFresh && neonProjectsCache
|
|
696
743
|
? neonProjectsCache.items
|
|
697
744
|
: await listAvailableNeonProjects({
|
|
698
745
|
projectDir: opts.projectDir,
|
|
@@ -707,7 +754,11 @@ async function handleRoute(req, res, opts) {
|
|
|
707
754
|
return;
|
|
708
755
|
}
|
|
709
756
|
// POST /schema/storage/buckets — create storage bucket
|
|
710
|
-
if (method === 'POST' &&
|
|
757
|
+
if (method === 'POST' &&
|
|
758
|
+
segments[0] === 'schema' &&
|
|
759
|
+
segments[1] === 'storage' &&
|
|
760
|
+
segments[2] === 'buckets' &&
|
|
761
|
+
segments.length === 3) {
|
|
711
762
|
const body = await readBody(req);
|
|
712
763
|
const name = body.name;
|
|
713
764
|
if (!name)
|
|
@@ -717,7 +768,10 @@ async function handleRoute(req, res, opts) {
|
|
|
717
768
|
return;
|
|
718
769
|
}
|
|
719
770
|
// DELETE /schema/tables/:name — delete table
|
|
720
|
-
if (method === 'DELETE' &&
|
|
771
|
+
if (method === 'DELETE' &&
|
|
772
|
+
segments[0] === 'schema' &&
|
|
773
|
+
segments[1] === 'tables' &&
|
|
774
|
+
segments.length === 3) {
|
|
721
775
|
const tableName = segments[2];
|
|
722
776
|
const body = await readBody(req);
|
|
723
777
|
const dbKey = body.dbKey || 'shared';
|
|
@@ -726,7 +780,11 @@ async function handleRoute(req, res, opts) {
|
|
|
726
780
|
return;
|
|
727
781
|
}
|
|
728
782
|
// PUT /schema/tables/:name/rename — rename table
|
|
729
|
-
if (method === 'PUT' &&
|
|
783
|
+
if (method === 'PUT' &&
|
|
784
|
+
segments[0] === 'schema' &&
|
|
785
|
+
segments[1] === 'tables' &&
|
|
786
|
+
segments[3] === 'rename' &&
|
|
787
|
+
segments.length === 4) {
|
|
730
788
|
const tableName = segments[2];
|
|
731
789
|
const body = await readBody(req);
|
|
732
790
|
const dbKey = body.dbKey || 'shared';
|
|
@@ -753,7 +811,11 @@ async function handleRoute(req, res, opts) {
|
|
|
753
811
|
return;
|
|
754
812
|
}
|
|
755
813
|
// POST /schema/tables/:name/columns — add column
|
|
756
|
-
if (method === 'POST' &&
|
|
814
|
+
if (method === 'POST' &&
|
|
815
|
+
segments[0] === 'schema' &&
|
|
816
|
+
segments[1] === 'tables' &&
|
|
817
|
+
segments[3] === 'columns' &&
|
|
818
|
+
segments.length === 4) {
|
|
757
819
|
const tableName = segments[2];
|
|
758
820
|
const body = await readBody(req);
|
|
759
821
|
const dbKey = body.dbKey || 'shared';
|
|
@@ -768,7 +830,11 @@ async function handleRoute(req, res, opts) {
|
|
|
768
830
|
return;
|
|
769
831
|
}
|
|
770
832
|
// PUT /schema/tables/:name/columns/:col — update column
|
|
771
|
-
if (method === 'PUT' &&
|
|
833
|
+
if (method === 'PUT' &&
|
|
834
|
+
segments[0] === 'schema' &&
|
|
835
|
+
segments[1] === 'tables' &&
|
|
836
|
+
segments[3] === 'columns' &&
|
|
837
|
+
segments.length === 5) {
|
|
772
838
|
const tableName = segments[2];
|
|
773
839
|
const columnName = segments[4];
|
|
774
840
|
const body = await readBody(req);
|
|
@@ -781,7 +847,11 @@ async function handleRoute(req, res, opts) {
|
|
|
781
847
|
return;
|
|
782
848
|
}
|
|
783
849
|
// DELETE /schema/tables/:name/columns/:col — remove column
|
|
784
|
-
if (method === 'DELETE' &&
|
|
850
|
+
if (method === 'DELETE' &&
|
|
851
|
+
segments[0] === 'schema' &&
|
|
852
|
+
segments[1] === 'tables' &&
|
|
853
|
+
segments[3] === 'columns' &&
|
|
854
|
+
segments.length === 5) {
|
|
785
855
|
const tableName = segments[2];
|
|
786
856
|
const columnName = segments[4];
|
|
787
857
|
const body = await readBody(req);
|
|
@@ -791,7 +861,11 @@ async function handleRoute(req, res, opts) {
|
|
|
791
861
|
return;
|
|
792
862
|
}
|
|
793
863
|
// POST /schema/tables/:name/indexes — add index
|
|
794
|
-
if (method === 'POST' &&
|
|
864
|
+
if (method === 'POST' &&
|
|
865
|
+
segments[0] === 'schema' &&
|
|
866
|
+
segments[1] === 'tables' &&
|
|
867
|
+
segments[3] === 'indexes' &&
|
|
868
|
+
segments.length === 4) {
|
|
795
869
|
const tableName = segments[2];
|
|
796
870
|
const body = await readBody(req);
|
|
797
871
|
const dbKey = body.dbKey || 'shared';
|
|
@@ -803,7 +877,11 @@ async function handleRoute(req, res, opts) {
|
|
|
803
877
|
return;
|
|
804
878
|
}
|
|
805
879
|
// DELETE /schema/tables/:name/indexes/:idx — remove index
|
|
806
|
-
if (method === 'DELETE' &&
|
|
880
|
+
if (method === 'DELETE' &&
|
|
881
|
+
segments[0] === 'schema' &&
|
|
882
|
+
segments[1] === 'tables' &&
|
|
883
|
+
segments[3] === 'indexes' &&
|
|
884
|
+
segments.length === 5) {
|
|
807
885
|
const tableName = segments[2];
|
|
808
886
|
const indexIdx = parseInt(segments[4], 10);
|
|
809
887
|
const body = await readBody(req);
|
|
@@ -813,7 +891,11 @@ async function handleRoute(req, res, opts) {
|
|
|
813
891
|
return;
|
|
814
892
|
}
|
|
815
893
|
// PUT /schema/tables/:name/fts — set FTS fields
|
|
816
|
-
if (method === 'PUT' &&
|
|
894
|
+
if (method === 'PUT' &&
|
|
895
|
+
segments[0] === 'schema' &&
|
|
896
|
+
segments[1] === 'tables' &&
|
|
897
|
+
segments[3] === 'fts' &&
|
|
898
|
+
segments.length === 4) {
|
|
817
899
|
const tableName = segments[2];
|
|
818
900
|
const body = await readBody(req);
|
|
819
901
|
const dbKey = body.dbKey || 'shared';
|
|
@@ -824,14 +906,20 @@ async function handleRoute(req, res, opts) {
|
|
|
824
906
|
}
|
|
825
907
|
// ─── Auth Settings Editing ───
|
|
826
908
|
// GET /auth/settings — read current auth config
|
|
827
|
-
if (method === 'GET' &&
|
|
909
|
+
if (method === 'GET' &&
|
|
910
|
+
segments[0] === 'auth' &&
|
|
911
|
+
segments[1] === 'settings' &&
|
|
912
|
+
segments.length === 2) {
|
|
828
913
|
const target = parseAuthSettingsTarget(url.searchParams.get('target'));
|
|
829
914
|
const config = loadSidecarConfig(opts, target);
|
|
830
915
|
json(res, 200, { ok: true, target, ...readAuthSettings(config) });
|
|
831
916
|
return;
|
|
832
917
|
}
|
|
833
918
|
// PUT /auth/settings — save auth config
|
|
834
|
-
if (method === 'PUT' &&
|
|
919
|
+
if (method === 'PUT' &&
|
|
920
|
+
segments[0] === 'auth' &&
|
|
921
|
+
segments[1] === 'settings' &&
|
|
922
|
+
segments.length === 2) {
|
|
835
923
|
const target = parseAuthSettingsTarget(url.searchParams.get('target'));
|
|
836
924
|
const body = await readBody(req);
|
|
837
925
|
const session = body.session;
|
|
@@ -903,7 +991,10 @@ async function handleRoute(req, res, opts) {
|
|
|
903
991
|
}
|
|
904
992
|
// ─── Email Template Editing ───
|
|
905
993
|
// PUT /email/templates — save email subject/template override (with optional locale)
|
|
906
|
-
if (method === 'PUT' &&
|
|
994
|
+
if (method === 'PUT' &&
|
|
995
|
+
segments[0] === 'email' &&
|
|
996
|
+
segments[1] === 'templates' &&
|
|
997
|
+
segments.length === 2) {
|
|
907
998
|
const body = await readBody(req);
|
|
908
999
|
const type = body.type;
|
|
909
1000
|
const locale = body.locale ?? 'en';
|
|
@@ -953,13 +1044,18 @@ async function handleRoute(req, res, opts) {
|
|
|
953
1044
|
return;
|
|
954
1045
|
}
|
|
955
1046
|
// GET /email/templates — read current email config
|
|
956
|
-
if (method === 'GET' &&
|
|
1047
|
+
if (method === 'GET' &&
|
|
1048
|
+
segments[0] === 'email' &&
|
|
1049
|
+
segments[1] === 'templates' &&
|
|
1050
|
+
segments.length === 2) {
|
|
957
1051
|
// Re-read the full config to get email section
|
|
958
1052
|
try {
|
|
959
1053
|
const projectDir = resolve(opts.configPath, '..');
|
|
1054
|
+
const moduleUrl = pathToFileURL(resolve(opts.configPath)).href;
|
|
960
1055
|
const result = execTsxSync([
|
|
961
1056
|
'-e',
|
|
962
|
-
`
|
|
1057
|
+
`(async()=>{const moduleUrl=process.argv[1];const mod=await import(moduleUrl);const d=mod.default??mod;const e=d.email??{};console.log(JSON.stringify({appName:e.appName||'EdgeBase',subjects:e.subjects||{},templates:e.templates||{}}));})().catch((error)=>{console.error(error instanceof Error?error.message:String(error));process.exit(1);});`,
|
|
1058
|
+
moduleUrl,
|
|
963
1059
|
], { cwd: projectDir, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
964
1060
|
const emailConfig = JSON.parse(result);
|
|
965
1061
|
json(res, 200, { ok: true, ...emailConfig });
|
|
@@ -1002,7 +1098,7 @@ export function startSidecar(opts) {
|
|
|
1002
1098
|
console.log(chalk.dim(` 📐 Schema Editor sidecar skipped (:${opts.port} already in use)`));
|
|
1003
1099
|
return;
|
|
1004
1100
|
}
|
|
1005
|
-
console.log(chalk.dim(' 📐 Schema Editor sidecar skipped:'), err.message);
|
|
1101
|
+
console.log(chalk.dim(' 📐 Schema Editor sidecar skipped:'), sanitizeForLog(err.message));
|
|
1006
1102
|
});
|
|
1007
1103
|
server.listen(opts.port, () => {
|
|
1008
1104
|
console.log(chalk.dim(` 📐 Schema Editor sidecar on :${opts.port}`));
|
|
@@ -1034,7 +1130,8 @@ export function parseEnvFile(filePath) {
|
|
|
1034
1130
|
const key = trimmed.slice(0, eqIdx).trim();
|
|
1035
1131
|
let value = trimmed.slice(eqIdx + 1).trim();
|
|
1036
1132
|
// Strip surrounding quotes
|
|
1037
|
-
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
1133
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
1134
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
1038
1135
|
value = value.slice(1, -1);
|
|
1039
1136
|
}
|
|
1040
1137
|
vars[key] = value;
|