@authrim/setup 0.1.140 → 0.1.142
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/dist/__tests__/keys.test.js +73 -2
- package/dist/__tests__/keys.test.js.map +1 -1
- package/dist/__tests__/migrate.test.js +4 -4
- package/dist/__tests__/migrate.test.js.map +1 -1
- package/dist/__tests__/paths.test.js +163 -1
- package/dist/__tests__/paths.test.js.map +1 -1
- package/dist/__tests__/source-context.test.d.ts +2 -0
- package/dist/__tests__/source-context.test.d.ts.map +1 -0
- package/dist/__tests__/source-context.test.js +72 -0
- package/dist/__tests__/source-context.test.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/deploy.js +65 -37
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +277 -198
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/core/admin.d.ts +6 -1
- package/dist/core/admin.d.ts.map +1 -1
- package/dist/core/admin.js +45 -20
- package/dist/core/admin.js.map +1 -1
- package/dist/core/cloudflare.d.ts +38 -1
- package/dist/core/cloudflare.d.ts.map +1 -1
- package/dist/core/cloudflare.js +729 -115
- package/dist/core/cloudflare.js.map +1 -1
- package/dist/core/config.d.ts +164 -34
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +72 -18
- package/dist/core/config.js.map +1 -1
- package/dist/core/deploy.d.ts +18 -0
- package/dist/core/deploy.d.ts.map +1 -1
- package/dist/core/deploy.js +126 -25
- package/dist/core/deploy.js.map +1 -1
- package/dist/core/keys.d.ts +20 -4
- package/dist/core/keys.d.ts.map +1 -1
- package/dist/core/keys.js +77 -17
- package/dist/core/keys.js.map +1 -1
- package/dist/core/login-ui-client.d.ts +42 -0
- package/dist/core/login-ui-client.d.ts.map +1 -0
- package/dist/core/login-ui-client.js +173 -0
- package/dist/core/login-ui-client.js.map +1 -0
- package/dist/core/migrate.d.ts +37 -0
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +92 -2
- package/dist/core/migrate.js.map +1 -1
- package/dist/core/paths.d.ts +78 -13
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +135 -17
- package/dist/core/paths.js.map +1 -1
- package/dist/core/source-context.d.ts +22 -0
- package/dist/core/source-context.d.ts.map +1 -0
- package/dist/core/source-context.js +46 -0
- package/dist/core/source-context.js.map +1 -0
- package/dist/core/tenant-mode.d.ts +4 -0
- package/dist/core/tenant-mode.d.ts.map +1 -0
- package/dist/core/tenant-mode.js +17 -0
- package/dist/core/tenant-mode.js.map +1 -0
- package/dist/core/ui-deployment.d.ts +21 -0
- package/dist/core/ui-deployment.d.ts.map +1 -0
- package/dist/core/ui-deployment.js +90 -0
- package/dist/core/ui-deployment.js.map +1 -0
- package/dist/core/ui-env.d.ts +28 -0
- package/dist/core/ui-env.d.ts.map +1 -1
- package/dist/core/ui-env.js +16 -0
- package/dist/core/ui-env.js.map +1 -1
- package/dist/core/url-config.d.ts +16 -0
- package/dist/core/url-config.d.ts.map +1 -0
- package/dist/core/url-config.js +46 -0
- package/dist/core/url-config.js.map +1 -0
- package/dist/core/wrangler.d.ts +50 -1
- package/dist/core/wrangler.d.ts.map +1 -1
- package/dist/core/wrangler.js +171 -57
- package/dist/core/wrangler.js.map +1 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/de.js +38 -1
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +38 -1
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/es.js +38 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts.map +1 -1
- package/dist/i18n/locales/fr.js +38 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/id.d.ts.map +1 -1
- package/dist/i18n/locales/id.js +38 -1
- package/dist/i18n/locales/id.js.map +1 -1
- package/dist/i18n/locales/ja.d.ts.map +1 -1
- package/dist/i18n/locales/ja.js +38 -1
- package/dist/i18n/locales/ja.js.map +1 -1
- package/dist/i18n/locales/ko.d.ts.map +1 -1
- package/dist/i18n/locales/ko.js +38 -1
- package/dist/i18n/locales/ko.js.map +1 -1
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/pt.js +38 -1
- package/dist/i18n/locales/pt.js.map +1 -1
- package/dist/i18n/locales/ru.d.ts.map +1 -1
- package/dist/i18n/locales/ru.js +38 -1
- package/dist/i18n/locales/ru.js.map +1 -1
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
- package/dist/i18n/locales/zh-CN.js +38 -1
- package/dist/i18n/locales/zh-CN.js.map +1 -1
- package/dist/i18n/locales/zh-TW.d.ts.map +1 -1
- package/dist/i18n/locales/zh-TW.js +38 -1
- package/dist/i18n/locales/zh-TW.js.map +1 -1
- package/dist/i18n/types.d.ts +8 -0
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -30
- package/dist/index.js.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +243 -116
- package/dist/web/api.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +513 -115
- package/dist/web/ui.js.map +1 -1
- package/migrations/000_fresh_schema.sql +229 -10
- package/migrations/admin/007_admin_role_inheritance.sql +32 -0
- package/migrations/admin/008_admin_rebac_definitions.sql +117 -0
- package/migrations/admin/009_optimize_admin_audit_indexes.sql +15 -0
- package/package.json +5 -5
package/dist/web/api.js
CHANGED
|
@@ -18,15 +18,19 @@ import { isWranglerInstalled, checkAuth, provisionResources, detectEnvironments,
|
|
|
18
18
|
import { AuthrimConfigSchema, createDefaultConfig, D1LocationSchema, D1JurisdictionSchema, } from '../core/config.js';
|
|
19
19
|
import { generateAllSecrets, saveKeysToDirectory, keysExistForEnvironment } from '../core/keys.js';
|
|
20
20
|
import { createLockFile, saveLockFile } from '../core/lock.js';
|
|
21
|
-
import { getEnvironmentPaths, resolvePaths, listEnvironments, findAuthrimBaseDir, } from '../core/paths.js';
|
|
21
|
+
import { getEnvironmentPaths, getExternalKeysDir, findKeysDirectory, resolvePaths, listEnvironments, findAuthrimBaseDir, } from '../core/paths.js';
|
|
22
22
|
import { generateWranglerConfig, toToml } from '../core/wrangler.js';
|
|
23
23
|
import { syncWranglerConfigs } from '../core/wrangler-sync.js';
|
|
24
|
+
import { buildUrlsConfig } from '../core/url-config.js';
|
|
25
|
+
import { normalizeTenantConfigForApiDomain } from '../core/tenant-mode.js';
|
|
24
26
|
import { deployAll, uploadSecrets, buildApiPackages, deployAllPages, deployPagesComponent, deployWorker, PAGES_COMPONENTS, } from '../core/deploy.js';
|
|
25
27
|
import { getEnabledComponents, WORKER_COMPONENTS } from '../core/naming.js';
|
|
26
28
|
import { getLocalPackageVersions, compareVersions, getComponentsToUpdate, } from '../core/version.js';
|
|
27
29
|
import { completeInitialSetup } from '../core/admin.js';
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
+
import { resolveUiDeploymentSettings } from '../core/ui-deployment.js';
|
|
31
|
+
import { saveUiEnv, buildInitialUiEnvConfig } from '../core/ui-env.js';
|
|
32
|
+
import { writeFile, chmod, mkdir } from 'node:fs/promises';
|
|
33
|
+
import { join, dirname } from 'node:path';
|
|
30
34
|
// =============================================================================
|
|
31
35
|
// Session & Security
|
|
32
36
|
// =============================================================================
|
|
@@ -117,6 +121,29 @@ export function createApiRoutes() {
|
|
|
117
121
|
api.use('/deploy', validateSession);
|
|
118
122
|
api.use('/reset', validateSession);
|
|
119
123
|
api.use('/admin/*', validateSession);
|
|
124
|
+
api.use('/cloudflare/*', validateSession);
|
|
125
|
+
// ==========================================================================
|
|
126
|
+
// Cloudflare Zone Check
|
|
127
|
+
// ==========================================================================
|
|
128
|
+
api.post('/cloudflare/check-zone', async (c) => {
|
|
129
|
+
try {
|
|
130
|
+
const body = (await c.req.json());
|
|
131
|
+
const { domain } = body;
|
|
132
|
+
if (!domain || typeof domain !== 'string') {
|
|
133
|
+
return c.json({ found: false, error: 'domain is required' }, 400);
|
|
134
|
+
}
|
|
135
|
+
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*\.[a-z]{2,}$/.test(domain)) {
|
|
136
|
+
return c.json({ found: false, error: 'Invalid domain format' }, 400);
|
|
137
|
+
}
|
|
138
|
+
const { checkZoneExists } = await import('../core/cloudflare.js');
|
|
139
|
+
const result = await checkZoneExists(domain);
|
|
140
|
+
return c.json(result);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
144
|
+
return c.json({ found: false, error: message }, 500);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
120
147
|
// Get current state (no auth required - read-only)
|
|
121
148
|
api.get('/state', (c) => {
|
|
122
149
|
return c.json(state);
|
|
@@ -248,10 +275,13 @@ export function createApiRoutes() {
|
|
|
248
275
|
// Use new structure for saving
|
|
249
276
|
const envPaths = getEnvironmentPaths({ baseDir, env });
|
|
250
277
|
// Ensure directory exists
|
|
251
|
-
const { mkdir } = await import('node:fs/promises');
|
|
252
278
|
await mkdir(envPaths.root, { recursive: true });
|
|
253
279
|
// Save config
|
|
254
280
|
await writeFile(envPaths.config, JSON.stringify(config, null, 2));
|
|
281
|
+
const initialUiEnv = buildInitialUiEnvConfig(config);
|
|
282
|
+
if (initialUiEnv) {
|
|
283
|
+
await saveUiEnv(envPaths.uiEnv, initialUiEnv);
|
|
284
|
+
}
|
|
255
285
|
state.config = config;
|
|
256
286
|
return c.json({ success: true, configPath: envPaths.config, structure: 'new' });
|
|
257
287
|
}
|
|
@@ -268,34 +298,21 @@ export function createApiRoutes() {
|
|
|
268
298
|
return withLock(async () => {
|
|
269
299
|
try {
|
|
270
300
|
const body = await c.req.json();
|
|
271
|
-
const { env = 'prod', apiDomain, loginUiDomain, adminUiDomain, tenant, components } = body;
|
|
301
|
+
const { env = 'prod', apiDomain, loginUiDomain, adminUiDomain, tenant, components, zoneId, customDomainBinding, } = body;
|
|
272
302
|
const config = createDefaultConfig(env);
|
|
273
303
|
// Update tenant configuration
|
|
274
304
|
if (tenant) {
|
|
275
|
-
config.tenant =
|
|
276
|
-
name: tenant.name || 'default',
|
|
277
|
-
displayName: tenant.displayName || 'Default Tenant',
|
|
278
|
-
multiTenant: tenant.multiTenant || false,
|
|
279
|
-
baseDomain: tenant.baseDomain,
|
|
280
|
-
};
|
|
305
|
+
config.tenant = normalizeTenantConfigForApiDomain(tenant);
|
|
281
306
|
}
|
|
282
307
|
// Update URLs with domain configuration
|
|
283
|
-
config.urls = {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
sameAsApi: false,
|
|
292
|
-
},
|
|
293
|
-
adminUi: {
|
|
294
|
-
custom: adminUiDomain || null,
|
|
295
|
-
auto: `https://${env}-ar-admin-ui.pages.dev`,
|
|
296
|
-
sameAsApi: false,
|
|
297
|
-
},
|
|
298
|
-
};
|
|
308
|
+
config.urls = buildUrlsConfig({
|
|
309
|
+
env,
|
|
310
|
+
apiDomain,
|
|
311
|
+
loginUiDomain,
|
|
312
|
+
adminUiDomain,
|
|
313
|
+
zoneId: zoneId || null,
|
|
314
|
+
customDomainBinding: customDomainBinding ?? false,
|
|
315
|
+
});
|
|
299
316
|
// Update components if provided
|
|
300
317
|
if (components) {
|
|
301
318
|
config.components = {
|
|
@@ -315,7 +332,8 @@ export function createApiRoutes() {
|
|
|
315
332
|
api.get('/keys/check/:env', async (c) => {
|
|
316
333
|
try {
|
|
317
334
|
const env = c.req.param('env');
|
|
318
|
-
const
|
|
335
|
+
const baseDir = findAuthrimBaseDir(process.cwd());
|
|
336
|
+
const exists = keysExistForEnvironment(baseDir, env, process.cwd());
|
|
319
337
|
return c.json({ exists, env });
|
|
320
338
|
}
|
|
321
339
|
catch (error) {
|
|
@@ -323,26 +341,27 @@ export function createApiRoutes() {
|
|
|
323
341
|
}
|
|
324
342
|
});
|
|
325
343
|
// Generate keys (with lock)
|
|
326
|
-
// Saves to
|
|
344
|
+
// Saves to external structure: {cwd}/.authrim-keys/{env}/
|
|
327
345
|
api.post('/keys/generate', async (c) => {
|
|
328
346
|
return withLock(async () => {
|
|
329
347
|
try {
|
|
330
348
|
const body = await c.req.json();
|
|
331
349
|
const { keyId, env } = body;
|
|
332
|
-
const
|
|
350
|
+
const envName = env || 'default';
|
|
351
|
+
const keysBaseDir = process.cwd();
|
|
333
352
|
addProgress('Generating cryptographic keys...');
|
|
334
353
|
const secrets = generateAllSecrets(keyId);
|
|
335
|
-
//
|
|
336
|
-
const
|
|
337
|
-
addProgress(`Saving keys to directory: ${
|
|
338
|
-
await saveKeysToDirectory(secrets, {
|
|
354
|
+
// Save to external keys directory: {cwd}/.authrim-keys/{env}/
|
|
355
|
+
const externalKeysDir = getExternalKeysDir(envName, keysBaseDir);
|
|
356
|
+
addProgress(`Saving keys to directory: ${externalKeysDir}/`);
|
|
357
|
+
await saveKeysToDirectory(secrets, { keysBaseDir, env: envName });
|
|
339
358
|
addProgress('Keys generated successfully');
|
|
340
359
|
// Only return public information
|
|
341
360
|
return c.json({
|
|
342
361
|
success: true,
|
|
343
362
|
keyId: secrets.keyPair.keyId,
|
|
344
363
|
publicKeyJwk: secrets.keyPair.publicKeyJwk,
|
|
345
|
-
keysPath:
|
|
364
|
+
keysPath: externalKeysDir,
|
|
346
365
|
});
|
|
347
366
|
}
|
|
348
367
|
catch (error) {
|
|
@@ -377,23 +396,26 @@ export function createApiRoutes() {
|
|
|
377
396
|
// Warning but not an error - just log it
|
|
378
397
|
addProgress('Warning: Resend API key should start with "re_"');
|
|
379
398
|
}
|
|
380
|
-
// Save secrets to keys directory
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
await mkdir(keysDir, { recursive: true });
|
|
387
|
-
// Save API key
|
|
399
|
+
// Save secrets to external keys directory: {cwd}/.authrim-keys/{env}/
|
|
400
|
+
const keysBaseDir = process.cwd();
|
|
401
|
+
const keysDir = getExternalKeysDir(env, keysBaseDir);
|
|
402
|
+
const baseDir = findAuthrimBaseDir(keysBaseDir);
|
|
403
|
+
const envPaths = getEnvironmentPaths({ baseDir, env, keysBaseDir });
|
|
404
|
+
// Ensure directory exists with restrictive permissions
|
|
405
|
+
await mkdir(keysDir, { recursive: true, mode: 0o700 });
|
|
406
|
+
// Save API key with restrictive permissions
|
|
388
407
|
await writeFile(envPaths.keyFiles.resendApiKey, apiKey.trim());
|
|
408
|
+
await chmod(envPaths.keyFiles.resendApiKey, 0o600);
|
|
389
409
|
addProgress(`Saved ${provider} API key to ${envPaths.keyFiles.resendApiKey}`);
|
|
390
|
-
// Save from address
|
|
410
|
+
// Save from address with restrictive permissions
|
|
391
411
|
await writeFile(envPaths.keyFiles.emailFrom, fromAddress.trim());
|
|
412
|
+
await chmod(envPaths.keyFiles.emailFrom, 0o600);
|
|
392
413
|
addProgress(`Saved email from address to ${envPaths.keyFiles.emailFrom}`);
|
|
393
414
|
// Save from name if provided
|
|
394
415
|
if (fromName) {
|
|
395
416
|
const fromNameFile = join(keysDir, 'email_from_name.txt');
|
|
396
417
|
await writeFile(fromNameFile, fromName.trim());
|
|
418
|
+
await chmod(fromNameFile, 0o600);
|
|
397
419
|
addProgress(`Saved email from name to ${fromNameFile}`);
|
|
398
420
|
}
|
|
399
421
|
addProgress('Email configuration saved successfully');
|
|
@@ -469,6 +491,8 @@ export function createApiRoutes() {
|
|
|
469
491
|
addProgress('Creating lock file...');
|
|
470
492
|
const lock = createLockFile(env, resources);
|
|
471
493
|
const rootDir = findAuthrimBaseDir(process.cwd());
|
|
494
|
+
const envPaths = getEnvironmentPaths({ baseDir: rootDir, env });
|
|
495
|
+
addProgress(`Saving lock.json to ${envPaths.lock} ...`);
|
|
472
496
|
await saveLockFile(lock, { env, baseDir: rootDir });
|
|
473
497
|
// Save config.json
|
|
474
498
|
addProgress('Saving config.json...');
|
|
@@ -491,16 +515,32 @@ export function createApiRoutes() {
|
|
|
491
515
|
const apiUrl = workersSubdomain
|
|
492
516
|
? `https://${env}-ar-router.${workersSubdomain}.workers.dev`
|
|
493
517
|
: `https://${env}-ar-router.workers.dev`;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
518
|
+
config.urls = buildUrlsConfig({
|
|
519
|
+
env,
|
|
520
|
+
apiDomain: config.urls?.api?.custom || null,
|
|
521
|
+
loginUiDomain: config.urls?.loginUi?.custom || null,
|
|
522
|
+
adminUiDomain: config.urls?.adminUi?.custom || null,
|
|
523
|
+
zoneId: config.urls?.api?.zoneId ?? null,
|
|
524
|
+
customDomainBinding: config.urls?.api?.customDomainBinding ?? false,
|
|
525
|
+
workersSubdomain,
|
|
526
|
+
existingUrls: {
|
|
527
|
+
api: {
|
|
528
|
+
...config.urls?.api,
|
|
529
|
+
auto: apiUrl,
|
|
530
|
+
},
|
|
531
|
+
loginUi: config.urls?.loginUi,
|
|
532
|
+
adminUi: config.urls?.adminUi,
|
|
533
|
+
},
|
|
534
|
+
});
|
|
501
535
|
addProgress(`Configured URLs: API=${apiUrl}`);
|
|
502
|
-
|
|
536
|
+
// Explicitly ensure directory exists (defense in depth; saveLockFile also creates it)
|
|
537
|
+
await mkdir(dirname(envPaths.config), { recursive: true });
|
|
538
|
+
addProgress(`Saving config.json to ${envPaths.config} ...`);
|
|
503
539
|
await writeFile(envPaths.config, JSON.stringify(config, null, 2), 'utf-8');
|
|
540
|
+
const initialUiEnv = buildInitialUiEnvConfig(config);
|
|
541
|
+
if (initialUiEnv) {
|
|
542
|
+
await saveUiEnv(envPaths.uiEnv, initialUiEnv);
|
|
543
|
+
}
|
|
504
544
|
state.config = config;
|
|
505
545
|
// Generate wrangler.toml files
|
|
506
546
|
addProgress('Generating wrangler.toml files...');
|
|
@@ -529,11 +569,18 @@ export function createApiRoutes() {
|
|
|
529
569
|
}
|
|
530
570
|
state.status = 'configuring';
|
|
531
571
|
addProgress('Provisioning complete!');
|
|
572
|
+
addProgress(`📁 Config saved: ${envPaths.config}`);
|
|
573
|
+
addProgress(`📁 Lock saved: ${envPaths.lock}`);
|
|
532
574
|
return c.json({
|
|
533
575
|
success: true,
|
|
534
576
|
resources,
|
|
535
577
|
lock,
|
|
536
578
|
config,
|
|
579
|
+
savedPaths: {
|
|
580
|
+
config: envPaths.config,
|
|
581
|
+
lock: envPaths.lock,
|
|
582
|
+
root: envPaths.root,
|
|
583
|
+
},
|
|
537
584
|
});
|
|
538
585
|
}
|
|
539
586
|
catch (error) {
|
|
@@ -634,11 +681,19 @@ export function createApiRoutes() {
|
|
|
634
681
|
addProgress('Packages built successfully');
|
|
635
682
|
}
|
|
636
683
|
// Upload secrets first (secrets are read but not stored in state)
|
|
637
|
-
// Check
|
|
684
|
+
// Check external (.authrim-keys/), new (.authrim/{env}/keys/), and legacy (.keys/{env}/) structures
|
|
638
685
|
const baseDir = findAuthrimBaseDir(process.cwd());
|
|
639
686
|
const resolved = resolvePaths({ baseDir, env });
|
|
640
687
|
let keysDir;
|
|
641
|
-
|
|
688
|
+
const foundKeys = findKeysDirectory({
|
|
689
|
+
env,
|
|
690
|
+
sourceDir: baseDir,
|
|
691
|
+
keysBaseDir: process.cwd(),
|
|
692
|
+
});
|
|
693
|
+
if (foundKeys) {
|
|
694
|
+
keysDir = foundKeys.path;
|
|
695
|
+
}
|
|
696
|
+
else if (resolved.type === 'new') {
|
|
642
697
|
keysDir = resolved.paths.keys;
|
|
643
698
|
}
|
|
644
699
|
else {
|
|
@@ -649,6 +704,7 @@ export function createApiRoutes() {
|
|
|
649
704
|
const secrets = {};
|
|
650
705
|
const secretFiles = [
|
|
651
706
|
{ file: join(keysDir, 'private.pem'), name: 'PRIVATE_KEY_PEM' },
|
|
707
|
+
{ file: join(keysDir, 'public.jwk.json'), name: 'PUBLIC_JWK_JSON' },
|
|
652
708
|
{ file: join(keysDir, 'rp_token_encryption_key.txt'), name: 'RP_TOKEN_ENCRYPTION_KEY' },
|
|
653
709
|
{ file: join(keysDir, 'admin_api_secret.txt'), name: 'ADMIN_API_SECRET' },
|
|
654
710
|
{ file: join(keysDir, 'key_manager_secret.txt'), name: 'KEY_MANAGER_SECRET' },
|
|
@@ -792,45 +848,70 @@ export function createApiRoutes() {
|
|
|
792
848
|
const apiBaseUrl = cfg?.urls?.api?.custom ||
|
|
793
849
|
cfg?.urls?.api?.auto ||
|
|
794
850
|
`https://${env}-ar-router.workers.dev`;
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
851
|
+
let loginUiClientId;
|
|
852
|
+
if (cfg?.components?.loginUi && !dryRun) {
|
|
853
|
+
const loginUiUrl = cfg?.urls?.loginUi?.custom ||
|
|
854
|
+
cfg?.urls?.loginUi?.auto ||
|
|
855
|
+
`https://${env}-ar-login-ui.pages.dev`;
|
|
856
|
+
const foundKeys = findKeysDirectory({
|
|
857
|
+
env,
|
|
858
|
+
sourceDir: rootDir,
|
|
859
|
+
keysBaseDir: process.cwd(),
|
|
860
|
+
});
|
|
861
|
+
const adminApiSecretPath = foundKeys
|
|
862
|
+
? join(foundKeys.path, 'admin_api_secret.txt')
|
|
863
|
+
: resolved.paths.keyFiles.adminApiSecret;
|
|
864
|
+
const { ensureLoginUiClient } = await import('../core/login-ui-client.js');
|
|
865
|
+
const clientResult = await ensureLoginUiClient({
|
|
866
|
+
apiBaseUrl,
|
|
867
|
+
loginUiUrl,
|
|
868
|
+
adminApiSecretPath,
|
|
869
|
+
onProgress: addProgress,
|
|
870
|
+
});
|
|
871
|
+
if (clientResult.success && clientResult.clientId) {
|
|
872
|
+
loginUiClientId = clientResult.clientId;
|
|
873
|
+
if (clientResult.alreadyExists) {
|
|
874
|
+
addProgress(` ✓ Login UI client exists: ${loginUiClientId}`);
|
|
814
875
|
}
|
|
815
876
|
else {
|
|
816
|
-
|
|
817
|
-
PUBLIC_API_BASE_URL: '', // Empty for proxy mode (same-origin)
|
|
818
|
-
API_BACKEND_URL: apiBaseUrl, // Server-side proxy target
|
|
819
|
-
});
|
|
820
|
-
addProgress(`ui.env synced (proxy mode: ${apiBaseUrl})`);
|
|
877
|
+
addProgress(` ✓ Login UI client created: ${loginUiClientId}`);
|
|
821
878
|
}
|
|
822
879
|
}
|
|
823
|
-
|
|
824
|
-
addProgress(
|
|
880
|
+
else {
|
|
881
|
+
addProgress(` ⚠️ Login UI client creation skipped: ${clientResult.error}`);
|
|
825
882
|
}
|
|
826
883
|
}
|
|
884
|
+
const loginUiSettings = resolveUiDeploymentSettings({
|
|
885
|
+
component: 'ar-login-ui',
|
|
886
|
+
config: cfg,
|
|
887
|
+
apiBaseUrl,
|
|
888
|
+
loginUiClientId,
|
|
889
|
+
});
|
|
890
|
+
const adminUiSettings = resolveUiDeploymentSettings({
|
|
891
|
+
component: 'ar-admin-ui',
|
|
892
|
+
config: cfg,
|
|
893
|
+
apiBaseUrl,
|
|
894
|
+
});
|
|
827
895
|
pagesSummary = await deployAllPages({
|
|
828
896
|
env,
|
|
829
897
|
rootDir: resolve(rootDir),
|
|
830
898
|
dryRun,
|
|
831
899
|
onProgress: addProgress,
|
|
832
900
|
apiBaseUrl,
|
|
833
|
-
|
|
901
|
+
perComponent: {
|
|
902
|
+
'ar-login-ui': {
|
|
903
|
+
apiBaseUrl: loginUiSettings.apiBaseUrl,
|
|
904
|
+
runtimeApiBackendUrl: loginUiSettings.runtimeApiBackendUrl,
|
|
905
|
+
uiEnvConfig: loginUiSettings.uiEnv,
|
|
906
|
+
serviceBindingName: loginUiSettings.serviceBindingName,
|
|
907
|
+
},
|
|
908
|
+
'ar-admin-ui': {
|
|
909
|
+
apiBaseUrl: adminUiSettings.apiBaseUrl,
|
|
910
|
+
runtimeApiBackendUrl: adminUiSettings.runtimeApiBackendUrl,
|
|
911
|
+
uiEnvConfig: adminUiSettings.uiEnv,
|
|
912
|
+
serviceBindingName: adminUiSettings.serviceBindingName,
|
|
913
|
+
},
|
|
914
|
+
},
|
|
834
915
|
}, {
|
|
835
916
|
loginUi: cfg?.components?.loginUi ?? true,
|
|
836
917
|
adminUi: cfg?.components?.adminUi ?? true,
|
|
@@ -855,10 +936,13 @@ export function createApiRoutes() {
|
|
|
855
936
|
// Update lock file with deployed workers information
|
|
856
937
|
if (workersSuccess && !dryRun && summary.successCount > 0) {
|
|
857
938
|
try {
|
|
858
|
-
const { loadLockFileAuto, saveLockFile: saveLock } = await import('../core/lock.js');
|
|
939
|
+
const { loadLockFileAuto, saveLockFile: saveLock, AuthrimLockSchema, } = await import('../core/lock.js');
|
|
859
940
|
const { lock: currentLock, path: lockPath } = await loadLockFileAuto(rootDir, env);
|
|
860
|
-
if (
|
|
861
|
-
|
|
941
|
+
if (lockPath) {
|
|
942
|
+
// Use existing lock or create minimal one (recovery scenario: lock deleted/missing)
|
|
943
|
+
const now = new Date().toISOString();
|
|
944
|
+
const baseLock = currentLock ?? AuthrimLockSchema.parse({ env, createdAt: now });
|
|
945
|
+
const workers = { ...baseLock.workers };
|
|
862
946
|
for (const result of summary.results) {
|
|
863
947
|
if (result.success && result.deployedAt) {
|
|
864
948
|
workers[result.component] = {
|
|
@@ -869,9 +953,9 @@ export function createApiRoutes() {
|
|
|
869
953
|
}
|
|
870
954
|
}
|
|
871
955
|
const updatedLock = {
|
|
872
|
-
...
|
|
956
|
+
...baseLock,
|
|
873
957
|
workers,
|
|
874
|
-
updatedAt:
|
|
958
|
+
updatedAt: now,
|
|
875
959
|
};
|
|
876
960
|
await saveLock(updatedLock, lockPath);
|
|
877
961
|
addProgress('Lock file updated with deployment info');
|
|
@@ -954,7 +1038,7 @@ export function createApiRoutes() {
|
|
|
954
1038
|
});
|
|
955
1039
|
});
|
|
956
1040
|
// Complete initial admin setup (store setup token in KV)
|
|
957
|
-
// Supports
|
|
1041
|
+
// Supports external (.authrim-keys/), internal (.authrim/{env}/keys/), and legacy (.keys/{env}/) structures
|
|
958
1042
|
api.post('/admin/setup', async (c) => {
|
|
959
1043
|
return withLock(async () => {
|
|
960
1044
|
try {
|
|
@@ -964,9 +1048,17 @@ export function createApiRoutes() {
|
|
|
964
1048
|
// Determine structure type
|
|
965
1049
|
const resolved = resolvePaths({ baseDir, env });
|
|
966
1050
|
const isLegacy = resolved.type === 'legacy';
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1051
|
+
// Detect actual token path using 3-tier fallback (external → internal → legacy)
|
|
1052
|
+
const foundKeys = findKeysDirectory({
|
|
1053
|
+
env,
|
|
1054
|
+
sourceDir: baseDir,
|
|
1055
|
+
keysBaseDir: process.cwd(),
|
|
1056
|
+
});
|
|
1057
|
+
const tokenPath = foundKeys
|
|
1058
|
+
? join(foundKeys.path, 'setup_token.txt')
|
|
1059
|
+
: isLegacy
|
|
1060
|
+
? resolved.paths.keyFiles.setupToken
|
|
1061
|
+
: resolved.paths.keyFiles.setupToken;
|
|
970
1062
|
addProgress(`Admin setup request: env=${env}, baseUrl=${baseUrl}, structure=${resolved.type}`);
|
|
971
1063
|
if (!env || !baseUrl) {
|
|
972
1064
|
addProgress('Error: env and baseUrl are required');
|
|
@@ -978,6 +1070,7 @@ export function createApiRoutes() {
|
|
|
978
1070
|
env,
|
|
979
1071
|
baseUrl,
|
|
980
1072
|
baseDir,
|
|
1073
|
+
keysBaseDir: process.cwd(),
|
|
981
1074
|
legacy: isLegacy,
|
|
982
1075
|
onProgress: addProgress,
|
|
983
1076
|
});
|
|
@@ -1032,7 +1125,7 @@ export function createApiRoutes() {
|
|
|
1032
1125
|
return withLock(async () => {
|
|
1033
1126
|
try {
|
|
1034
1127
|
const body = await c.req.json();
|
|
1035
|
-
const { kvNamespaceId, baseUrl } = body;
|
|
1128
|
+
const { kvNamespaceId, baseUrl, env: envName } = body;
|
|
1036
1129
|
if (!kvNamespaceId || !/^[a-f0-9]{32}$/i.test(kvNamespaceId)) {
|
|
1037
1130
|
return c.json({ success: false, error: 'Invalid KV namespace ID' }, 400);
|
|
1038
1131
|
}
|
|
@@ -1053,8 +1146,29 @@ export function createApiRoutes() {
|
|
|
1053
1146
|
if (!result.success || !result.token) {
|
|
1054
1147
|
return c.json({ success: false, error: result.error || 'Failed to generate token' }, 500);
|
|
1055
1148
|
}
|
|
1149
|
+
// Resolve the best base URL: prefer custom API domain from config
|
|
1150
|
+
let resolvedBaseUrl = baseUrl;
|
|
1151
|
+
if (envName) {
|
|
1152
|
+
try {
|
|
1153
|
+
const baseDir = findAuthrimBaseDir(process.cwd());
|
|
1154
|
+
const resolved = resolvePaths({ baseDir, env: envName });
|
|
1155
|
+
const configPath = resolved.type === 'new'
|
|
1156
|
+
? resolved.paths.config
|
|
1157
|
+
: resolved.paths.config;
|
|
1158
|
+
if (existsSync(configPath)) {
|
|
1159
|
+
const cfg = JSON.parse(await readFile(configPath, 'utf-8'));
|
|
1160
|
+
const configBaseUrl = cfg?.urls?.api?.custom || cfg?.urls?.api?.auto;
|
|
1161
|
+
if (configBaseUrl) {
|
|
1162
|
+
resolvedBaseUrl = configBaseUrl;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
catch {
|
|
1167
|
+
// Config not available, use the provided baseUrl
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1056
1170
|
// Construct setup URL
|
|
1057
|
-
const cleanBaseUrl =
|
|
1171
|
+
const cleanBaseUrl = resolvedBaseUrl.replace(/\/+$/, '');
|
|
1058
1172
|
const setupUrl = `${cleanBaseUrl}/admin-init-setup?token=${result.token}`;
|
|
1059
1173
|
return c.json({
|
|
1060
1174
|
success: true,
|
|
@@ -1440,40 +1554,53 @@ export function createApiRoutes() {
|
|
|
1440
1554
|
const apiBaseUrl = cfg?.urls?.api?.custom ||
|
|
1441
1555
|
cfg?.urls?.api?.auto ||
|
|
1442
1556
|
`https://${env}-ar-router.workers.dev`;
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1557
|
+
let loginUiClientId;
|
|
1558
|
+
if (componentName === 'ar-login-ui' && !dryRun) {
|
|
1559
|
+
const loginUiUrl = cfg?.urls?.loginUi?.custom ||
|
|
1560
|
+
cfg?.urls?.loginUi?.auto ||
|
|
1561
|
+
`https://${env}-ar-login-ui.pages.dev`;
|
|
1562
|
+
const foundKeys = findKeysDirectory({
|
|
1563
|
+
env,
|
|
1564
|
+
sourceDir: rootDir,
|
|
1565
|
+
keysBaseDir: process.cwd(),
|
|
1566
|
+
});
|
|
1567
|
+
const adminApiSecretPath = foundKeys
|
|
1568
|
+
? join(foundKeys.path, 'admin_api_secret.txt')
|
|
1569
|
+
: resolved.paths.keyFiles.adminApiSecret;
|
|
1570
|
+
const { ensureLoginUiClient } = await import('../core/login-ui-client.js');
|
|
1571
|
+
const clientResult = await ensureLoginUiClient({
|
|
1572
|
+
apiBaseUrl,
|
|
1573
|
+
loginUiUrl,
|
|
1574
|
+
adminApiSecretPath,
|
|
1575
|
+
onProgress: addProgress,
|
|
1576
|
+
});
|
|
1577
|
+
if (clientResult.success && clientResult.clientId) {
|
|
1578
|
+
loginUiClientId = clientResult.clientId;
|
|
1579
|
+
if (clientResult.alreadyExists) {
|
|
1580
|
+
addProgress(` ✓ Login UI client exists: ${loginUiClientId}`);
|
|
1458
1581
|
}
|
|
1459
1582
|
else {
|
|
1460
|
-
|
|
1461
|
-
PUBLIC_API_BASE_URL: '',
|
|
1462
|
-
API_BACKEND_URL: apiBaseUrl,
|
|
1463
|
-
});
|
|
1583
|
+
addProgress(` ✓ Login UI client created: ${loginUiClientId}`);
|
|
1464
1584
|
}
|
|
1465
|
-
addProgress(`ui.env synced for ${componentName}`);
|
|
1466
1585
|
}
|
|
1467
|
-
|
|
1468
|
-
addProgress(`
|
|
1586
|
+
else {
|
|
1587
|
+
addProgress(` ⚠️ Login UI client creation skipped: ${clientResult.error}`);
|
|
1469
1588
|
}
|
|
1470
1589
|
}
|
|
1590
|
+
const uiSettings = resolveUiDeploymentSettings({
|
|
1591
|
+
component: componentName,
|
|
1592
|
+
config: cfg,
|
|
1593
|
+
apiBaseUrl,
|
|
1594
|
+
loginUiClientId,
|
|
1595
|
+
});
|
|
1471
1596
|
const result = await deployPagesComponent(componentName, {
|
|
1472
1597
|
env,
|
|
1473
1598
|
rootDir,
|
|
1474
1599
|
dryRun,
|
|
1475
|
-
apiBaseUrl,
|
|
1476
|
-
|
|
1600
|
+
apiBaseUrl: uiSettings.apiBaseUrl,
|
|
1601
|
+
runtimeApiBackendUrl: uiSettings.runtimeApiBackendUrl,
|
|
1602
|
+
uiEnvConfig: uiSettings.uiEnv,
|
|
1603
|
+
serviceBindingName: uiSettings.serviceBindingName,
|
|
1477
1604
|
onProgress: addProgress,
|
|
1478
1605
|
});
|
|
1479
1606
|
if (result.success) {
|