@authrim/setup 0.1.141 → 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.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.map +1 -1
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/deploy.js +57 -63
- 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 +231 -171
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/core/admin.d.ts.map +1 -1
- package/dist/core/admin.js +13 -3
- 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 +136 -28
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +58 -11
- 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.map +1 -1
- package/dist/core/keys.js +2 -0
- package/dist/core/keys.js.map +1 -1
- package/dist/core/login-ui-client.d.ts.map +1 -1
- package/dist/core/login-ui-client.js +43 -7
- package/dist/core/login-ui-client.js.map +1 -1
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +5 -5
- package/dist/core/paths.js.map +1 -1
- 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 +17 -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 +169 -55
- package/dist/core/wrangler.js.map +1 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/de.js +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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 +37 -0
- 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.js +38 -29
- package/dist/index.js.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +207 -95
- package/dist/web/api.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +506 -109
- package/dist/web/ui.js.map +1 -1
- package/migrations/000_fresh_schema.sql +227 -9
- package/migrations/admin/006_admin_setup_tokens.sql +91 -91
- 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
|
@@ -15,11 +15,54 @@ import { execa } from 'execa';
|
|
|
15
15
|
import { createDefaultConfig, parseConfig } from '../../core/config.js';
|
|
16
16
|
import { generateAllSecrets, saveKeysToDirectory, generateKeyId, keysExistForEnvironment, } from '../../core/keys.js';
|
|
17
17
|
import { isRunningFromSource, getCommandPrefix } from '../../core/source-context.js';
|
|
18
|
-
import { isWranglerInstalled, checkAuth, provisionResources, toResourceIds, getAccountId, detectEnvironments, getWorkersSubdomain, } from '../../core/cloudflare.js';
|
|
18
|
+
import { isWranglerInstalled, checkAuth, provisionResources, toResourceIds, getAccountId, detectEnvironments, getWorkersSubdomain, checkZoneExists, extractZoneName, } from '../../core/cloudflare.js';
|
|
19
19
|
import { createLockFile, saveLockFile, loadLockFile } from '../../core/lock.js';
|
|
20
|
-
import { getEnvironmentPaths, getExternalKeysDir, getExternalKeysPathForConfig, AUTHRIM_DIR } from '../../core/paths.js';
|
|
20
|
+
import { getEnvironmentPaths, getExternalKeysDir, getExternalKeysPathForConfig, AUTHRIM_DIR, } from '../../core/paths.js';
|
|
21
21
|
import { downloadSource, verifySourceStructure, checkForUpdate, getLocalVersion, } from '../../core/source.js';
|
|
22
|
-
import { saveUiEnv } from '../../core/ui-env.js';
|
|
22
|
+
import { saveUiEnv, buildInitialUiEnvConfig } from '../../core/ui-env.js';
|
|
23
|
+
import { buildUrlsConfig, getPagesDevUrl, getWorkersDevUrl, } from '../../core/url-config.js';
|
|
24
|
+
/**
|
|
25
|
+
* Check Cloudflare zone for a domain and prompt user for binding configuration.
|
|
26
|
+
* Never blocks setup - all errors are handled gracefully.
|
|
27
|
+
*/
|
|
28
|
+
async function checkAndPromptZone(domain, domainConfig) {
|
|
29
|
+
const spinner = ora(t('domain.checkingZone', { domain })).start();
|
|
30
|
+
try {
|
|
31
|
+
const result = await checkZoneExists(domain);
|
|
32
|
+
spinner.stop();
|
|
33
|
+
if (result.error) {
|
|
34
|
+
console.log(chalk.yellow(` ⚠ ${t('domain.zoneCheckFailed')}: ${result.error}`));
|
|
35
|
+
console.log(chalk.gray(` ${t('domain.zoneCheckSkipped')}`));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!result.found) {
|
|
39
|
+
const zoneName = extractZoneName(domain);
|
|
40
|
+
console.log(chalk.yellow(` ⚠ ${t('domain.zoneNotFound', { zone: zoneName })}`));
|
|
41
|
+
console.log(chalk.gray(` ${t('domain.zoneNotFoundHint')}`));
|
|
42
|
+
console.log('');
|
|
43
|
+
const ok = await confirm({ message: t('domain.continueWithoutZone'), default: true });
|
|
44
|
+
if (!ok) {
|
|
45
|
+
throw new Error('USER_CANCELLED_DOMAIN');
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Zone found
|
|
50
|
+
console.log(chalk.green(` ✓ ${t('domain.zoneFound', { zone: result.zone.name, status: result.zone.status })}`));
|
|
51
|
+
domainConfig.zoneId = result.zone.id;
|
|
52
|
+
console.log('');
|
|
53
|
+
const bind = await confirm({ message: t('domain.configureBinding'), default: true });
|
|
54
|
+
domainConfig.customDomainBinding = bind;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
spinner.stop();
|
|
58
|
+
if (error instanceof Error && error.message === 'USER_CANCELLED_DOMAIN') {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
// Unexpected error - don't block setup
|
|
62
|
+
console.log(chalk.yellow(` ⚠ ${t('domain.zoneCheckFailed')}`));
|
|
63
|
+
console.log(chalk.gray(` ${t('domain.zoneCheckSkipped')}`));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
23
66
|
// =============================================================================
|
|
24
67
|
// WSL Detection
|
|
25
68
|
// =============================================================================
|
|
@@ -139,21 +182,12 @@ function printBanner() {
|
|
|
139
182
|
// Store the workers.dev subdomain for URL generation
|
|
140
183
|
let workersSubdomain = null;
|
|
141
184
|
/**
|
|
142
|
-
*
|
|
143
|
-
* Format: {worker}.{subdomain}.workers.dev
|
|
185
|
+
* Strip the protocol from a URL for display in domain-only prompts.
|
|
144
186
|
*/
|
|
145
|
-
function
|
|
146
|
-
if (
|
|
147
|
-
return
|
|
148
|
-
|
|
149
|
-
return `https://${workerName}.workers.dev`;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Get the correct pages.dev URL
|
|
153
|
-
* Note: Pages uses {project}.pages.dev format (no account subdomain, unlike Workers)
|
|
154
|
-
*/
|
|
155
|
-
function getPagesDevUrl(projectName) {
|
|
156
|
-
return `https://${projectName}.pages.dev`;
|
|
187
|
+
function stripProtocol(url) {
|
|
188
|
+
if (!url)
|
|
189
|
+
return '';
|
|
190
|
+
return url.replace(/^https?:\/\//, '');
|
|
157
191
|
}
|
|
158
192
|
// =============================================================================
|
|
159
193
|
// Source Directory Detection
|
|
@@ -978,6 +1012,7 @@ async function runQuickSetup(options) {
|
|
|
978
1012
|
let apiDomain = null;
|
|
979
1013
|
let loginUiDomain = null;
|
|
980
1014
|
let adminUiDomain = null;
|
|
1015
|
+
const quickDomainConfig = {};
|
|
981
1016
|
if (useCustomDomain) {
|
|
982
1017
|
console.log('');
|
|
983
1018
|
console.log(chalk.gray(' ' + t('domain.singleTenantNote')));
|
|
@@ -993,6 +1028,18 @@ async function runQuickSetup(options) {
|
|
|
993
1028
|
return true;
|
|
994
1029
|
},
|
|
995
1030
|
});
|
|
1031
|
+
// Check Cloudflare zone for the domain
|
|
1032
|
+
if (apiDomain) {
|
|
1033
|
+
console.log('');
|
|
1034
|
+
try {
|
|
1035
|
+
await checkAndPromptZone(apiDomain, quickDomainConfig);
|
|
1036
|
+
}
|
|
1037
|
+
catch {
|
|
1038
|
+
// User cancelled - clear domain and continue
|
|
1039
|
+
apiDomain = null;
|
|
1040
|
+
}
|
|
1041
|
+
console.log('');
|
|
1042
|
+
}
|
|
996
1043
|
loginUiDomain = await input({
|
|
997
1044
|
message: t('domain.loginUiDomain'),
|
|
998
1045
|
default: '',
|
|
@@ -1112,22 +1159,15 @@ async function runQuickSetup(options) {
|
|
|
1112
1159
|
configured: emailConfig.provider === 'resend',
|
|
1113
1160
|
},
|
|
1114
1161
|
};
|
|
1115
|
-
config.urls = {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
},
|
|
1125
|
-
adminUi: {
|
|
1126
|
-
custom: adminUiDomain || null,
|
|
1127
|
-
auto: getPagesDevUrl(envPrefix + '-ar-admin-ui'),
|
|
1128
|
-
sameAsApi: false,
|
|
1129
|
-
},
|
|
1130
|
-
};
|
|
1162
|
+
config.urls = buildUrlsConfig({
|
|
1163
|
+
env: envPrefix,
|
|
1164
|
+
apiDomain,
|
|
1165
|
+
loginUiDomain,
|
|
1166
|
+
adminUiDomain,
|
|
1167
|
+
zoneId: quickDomainConfig.zoneId ?? null,
|
|
1168
|
+
customDomainBinding: quickDomainConfig.customDomainBinding ?? false,
|
|
1169
|
+
workersSubdomain,
|
|
1170
|
+
});
|
|
1131
1171
|
// Show summary
|
|
1132
1172
|
console.log('');
|
|
1133
1173
|
console.log(chalk.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
@@ -1278,44 +1318,58 @@ async function runNormalSetup(options) {
|
|
|
1278
1318
|
// Step 5: Tenant configuration
|
|
1279
1319
|
console.log(chalk.blue('━━━ ' + t('tenant.title') + ' ━━━'));
|
|
1280
1320
|
console.log('');
|
|
1281
|
-
const multiTenant = await confirm({
|
|
1282
|
-
message: t('tenant.multiTenantPrompt'),
|
|
1283
|
-
default: false,
|
|
1284
|
-
});
|
|
1285
1321
|
let tenantName = 'default';
|
|
1286
1322
|
let tenantDisplayName = 'Default Tenant';
|
|
1287
1323
|
let baseDomain;
|
|
1288
|
-
|
|
1324
|
+
let primaryTenant;
|
|
1325
|
+
let nakedDomain = false;
|
|
1326
|
+
let userIdFormat = 'nanoid';
|
|
1327
|
+
// Step 6: URL configuration
|
|
1289
1328
|
let apiDomain = null;
|
|
1290
1329
|
let loginUiDomain = null;
|
|
1291
1330
|
let adminUiDomain = null;
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
return t('tenant.baseDomainRequired');
|
|
1307
|
-
if (!/^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(value)) {
|
|
1308
|
-
return t('tenant.baseDomainValidation');
|
|
1309
|
-
}
|
|
1331
|
+
const fullDomainConfig = {};
|
|
1332
|
+
// Base domain configuration
|
|
1333
|
+
console.log('');
|
|
1334
|
+
console.log(chalk.blue('━━━ ' + t('tenant.multiTenantTitle') + ' ━━━'));
|
|
1335
|
+
console.log('');
|
|
1336
|
+
console.log(chalk.gray(' Leave empty to use workers.dev and single-tenant mode.'));
|
|
1337
|
+
console.log(chalk.gray(' With a custom domain:'));
|
|
1338
|
+
console.log(chalk.gray(' • https://example.com (naked domain issuer)'));
|
|
1339
|
+
console.log(chalk.gray(' • https://acme.example.com (tenant subdomain issuer)'));
|
|
1340
|
+
console.log('');
|
|
1341
|
+
baseDomain = await input({
|
|
1342
|
+
message: t('tenant.baseDomainPrompt'),
|
|
1343
|
+
validate: (value) => {
|
|
1344
|
+
if (!value)
|
|
1310
1345
|
return true;
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1346
|
+
if (!/^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(value)) {
|
|
1347
|
+
return t('tenant.baseDomainValidation');
|
|
1348
|
+
}
|
|
1349
|
+
return true;
|
|
1350
|
+
},
|
|
1351
|
+
});
|
|
1352
|
+
baseDomain = baseDomain || undefined;
|
|
1353
|
+
console.log('');
|
|
1354
|
+
if (baseDomain) {
|
|
1355
|
+
console.log(chalk.green(' ✓ Base domain: ' + baseDomain));
|
|
1356
|
+
}
|
|
1357
|
+
else {
|
|
1358
|
+
console.log(chalk.green(' ✓ Using workers.dev (single-tenant mode)'));
|
|
1359
|
+
}
|
|
1360
|
+
// Check Cloudflare zone for the base domain
|
|
1361
|
+
if (baseDomain) {
|
|
1362
|
+
try {
|
|
1363
|
+
await checkAndPromptZone(baseDomain, fullDomainConfig);
|
|
1364
|
+
}
|
|
1365
|
+
catch {
|
|
1366
|
+
// User cancelled - this is non-fatal, continue with setup
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
console.log('');
|
|
1370
|
+
// API domain is the base domain
|
|
1371
|
+
apiDomain = baseDomain || null;
|
|
1372
|
+
if (baseDomain) {
|
|
1319
1373
|
tenantName = await input({
|
|
1320
1374
|
message: t('tenant.defaultTenantPrompt'),
|
|
1321
1375
|
default: 'default',
|
|
@@ -1326,80 +1380,78 @@ async function runNormalSetup(options) {
|
|
|
1326
1380
|
return true;
|
|
1327
1381
|
},
|
|
1328
1382
|
});
|
|
1329
|
-
tenantDisplayName = await input({
|
|
1330
|
-
message: t('tenant.displayNamePrompt'),
|
|
1331
|
-
default: 'Default Tenant',
|
|
1332
|
-
});
|
|
1333
|
-
// UI domains for multi-tenant
|
|
1334
|
-
console.log('');
|
|
1335
|
-
console.log(chalk.blue('━━━ ' + t('tenant.uiDomainTitle') + ' ━━━'));
|
|
1336
|
-
console.log('');
|
|
1337
|
-
const useCustomUiDomain = await confirm({
|
|
1338
|
-
message: t('tenant.customUiDomainPrompt'),
|
|
1339
|
-
default: false,
|
|
1340
|
-
});
|
|
1341
|
-
if (useCustomUiDomain) {
|
|
1342
|
-
loginUiDomain = await input({
|
|
1343
|
-
message: t('tenant.loginUiDomain'),
|
|
1344
|
-
default: '',
|
|
1345
|
-
});
|
|
1346
|
-
adminUiDomain = await input({
|
|
1347
|
-
message: t('tenant.adminUiDomain'),
|
|
1348
|
-
default: '',
|
|
1349
|
-
});
|
|
1350
|
-
}
|
|
1351
1383
|
}
|
|
1352
1384
|
else {
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
message: t('tenant.organizationName'),
|
|
1363
|
-
default: 'Default Tenant',
|
|
1364
|
-
});
|
|
1365
|
-
const useCustomDomain = await confirm({
|
|
1366
|
-
message: t('domain.prompt'),
|
|
1385
|
+
tenantName = 'default';
|
|
1386
|
+
}
|
|
1387
|
+
tenantDisplayName = await input({
|
|
1388
|
+
message: t('tenant.displayNamePrompt'),
|
|
1389
|
+
default: 'Default Tenant',
|
|
1390
|
+
});
|
|
1391
|
+
if (baseDomain) {
|
|
1392
|
+
nakedDomain = await confirm({
|
|
1393
|
+
message: 'Use naked domain as the issuer for the primary tenant?',
|
|
1367
1394
|
default: false,
|
|
1368
1395
|
});
|
|
1369
|
-
if (
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
apiDomain = await input({
|
|
1374
|
-
message: t('domain.apiDomain'),
|
|
1396
|
+
if (nakedDomain) {
|
|
1397
|
+
primaryTenant = await input({
|
|
1398
|
+
message: 'Primary tenant ID for naked domain (leave empty for default tenant)',
|
|
1399
|
+
default: '',
|
|
1375
1400
|
validate: (value) => {
|
|
1376
|
-
if (!value)
|
|
1401
|
+
if (!value) {
|
|
1377
1402
|
return true;
|
|
1378
|
-
|
|
1379
|
-
|
|
1403
|
+
}
|
|
1404
|
+
if (!/^[a-z][a-z0-9-]*$/.test(value)) {
|
|
1405
|
+
return 'Tenant ID must start with a letter and contain only lowercase letters, numbers, and hyphens';
|
|
1380
1406
|
}
|
|
1381
1407
|
return true;
|
|
1382
1408
|
},
|
|
1383
1409
|
});
|
|
1384
|
-
|
|
1385
|
-
message: t('domain.loginUiDomain'),
|
|
1386
|
-
default: '',
|
|
1387
|
-
});
|
|
1388
|
-
adminUiDomain = await input({
|
|
1389
|
-
message: t('domain.adminUiDomain'),
|
|
1390
|
-
default: '',
|
|
1391
|
-
});
|
|
1392
|
-
}
|
|
1393
|
-
if (apiDomain) {
|
|
1394
|
-
console.log('');
|
|
1395
|
-
console.log(chalk.green(' ✓ ' + t('domain.issuerUrl', { url: 'https://' + apiDomain })));
|
|
1396
|
-
}
|
|
1397
|
-
else {
|
|
1398
|
-
console.log('');
|
|
1399
|
-
console.log(chalk.green(' ✓ ' + t('domain.issuerUrl', { url: getWorkersDevUrl(envPrefix + '-ar-router') })));
|
|
1400
|
-
console.log(chalk.gray(' ' + t('domain.usingWorkersDev')));
|
|
1410
|
+
primaryTenant = primaryTenant || undefined;
|
|
1401
1411
|
}
|
|
1402
1412
|
}
|
|
1413
|
+
// User ID format selection
|
|
1414
|
+
console.log('');
|
|
1415
|
+
console.log(chalk.blue('━━━ ' + t('userId.title') + ' ━━━'));
|
|
1416
|
+
console.log('');
|
|
1417
|
+
console.log(chalk.gray(' ' + t('userId.note')));
|
|
1418
|
+
console.log('');
|
|
1419
|
+
userIdFormat = await select({
|
|
1420
|
+
message: t('userId.prompt'),
|
|
1421
|
+
choices: [
|
|
1422
|
+
{
|
|
1423
|
+
name: t('userId.nanoid'),
|
|
1424
|
+
value: 'nanoid',
|
|
1425
|
+
description: t('userId.nanoidDesc'),
|
|
1426
|
+
},
|
|
1427
|
+
{
|
|
1428
|
+
name: t('userId.uuid'),
|
|
1429
|
+
value: 'uuid',
|
|
1430
|
+
description: t('userId.uuidDesc'),
|
|
1431
|
+
},
|
|
1432
|
+
],
|
|
1433
|
+
default: 'nanoid',
|
|
1434
|
+
});
|
|
1435
|
+
console.log('');
|
|
1436
|
+
console.log(chalk.green(' ✓ ' + t('userId.selected', { format: userIdFormat })));
|
|
1437
|
+
// UI domains
|
|
1438
|
+
console.log('');
|
|
1439
|
+
console.log(chalk.blue('━━━ ' + t('tenant.uiDomainTitle') + ' ━━━'));
|
|
1440
|
+
console.log('');
|
|
1441
|
+
const useCustomUiDomain = await confirm({
|
|
1442
|
+
message: t('tenant.customUiDomainPrompt'),
|
|
1443
|
+
default: false,
|
|
1444
|
+
});
|
|
1445
|
+
if (useCustomUiDomain) {
|
|
1446
|
+
loginUiDomain = await input({
|
|
1447
|
+
message: t('tenant.loginUiDomain'),
|
|
1448
|
+
default: '',
|
|
1449
|
+
});
|
|
1450
|
+
adminUiDomain = await input({
|
|
1451
|
+
message: t('tenant.adminUiDomain'),
|
|
1452
|
+
default: '',
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1403
1455
|
// Step 5: Optional components
|
|
1404
1456
|
console.log('');
|
|
1405
1457
|
console.log(chalk.blue('━━━ ' + t('components.title') + ' ━━━'));
|
|
@@ -1660,8 +1712,11 @@ async function runNormalSetup(options) {
|
|
|
1660
1712
|
config.tenant = {
|
|
1661
1713
|
name: tenantName,
|
|
1662
1714
|
displayName: tenantDisplayName,
|
|
1663
|
-
multiTenant,
|
|
1715
|
+
multiTenant: !!baseDomain,
|
|
1664
1716
|
baseDomain,
|
|
1717
|
+
userIdFormat,
|
|
1718
|
+
primaryTenant,
|
|
1719
|
+
nakedDomain,
|
|
1665
1720
|
};
|
|
1666
1721
|
config.components = {
|
|
1667
1722
|
...config.components,
|
|
@@ -1671,22 +1726,15 @@ async function runNormalSetup(options) {
|
|
|
1671
1726
|
bridge: true, // Standard component
|
|
1672
1727
|
policy: true, // Standard component
|
|
1673
1728
|
};
|
|
1674
|
-
config.urls = {
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
},
|
|
1684
|
-
adminUi: {
|
|
1685
|
-
custom: adminUiDomain || null,
|
|
1686
|
-
auto: getPagesDevUrl(envPrefix + '-ar-admin-ui'),
|
|
1687
|
-
sameAsApi: false,
|
|
1688
|
-
},
|
|
1689
|
-
};
|
|
1729
|
+
config.urls = buildUrlsConfig({
|
|
1730
|
+
env: envPrefix,
|
|
1731
|
+
apiDomain,
|
|
1732
|
+
loginUiDomain,
|
|
1733
|
+
adminUiDomain,
|
|
1734
|
+
zoneId: fullDomainConfig.zoneId ?? null,
|
|
1735
|
+
customDomainBinding: fullDomainConfig.customDomainBinding ?? false,
|
|
1736
|
+
workersSubdomain,
|
|
1737
|
+
});
|
|
1690
1738
|
config.oidc = {
|
|
1691
1739
|
...config.oidc,
|
|
1692
1740
|
accessTokenTtl,
|
|
@@ -1729,14 +1777,17 @@ async function runNormalSetup(options) {
|
|
|
1729
1777
|
console.log('');
|
|
1730
1778
|
// Tenant mode and Issuer
|
|
1731
1779
|
console.log(chalk.bold('Tenant & Issuer:'));
|
|
1732
|
-
console.log(` Mode: ${
|
|
1733
|
-
if (
|
|
1780
|
+
console.log(` Mode: ${chalk.cyan(baseDomain ? 'Multi-tenant' : 'Single-tenant')}`);
|
|
1781
|
+
if (baseDomain) {
|
|
1734
1782
|
console.log(` Base Domain: ${chalk.cyan(baseDomain)}`);
|
|
1735
|
-
console.log(`
|
|
1736
|
-
console.log(` Example: ${chalk.gray('https://acme.' + baseDomain)}`);
|
|
1783
|
+
console.log(` Domain Pattern: ${chalk.cyan('{tenant}.' + baseDomain)}`);
|
|
1784
|
+
console.log(` Example: ${chalk.gray(nakedDomain ? 'https://' + baseDomain : 'https://acme.' + baseDomain)}`);
|
|
1785
|
+
if (nakedDomain) {
|
|
1786
|
+
console.log(` Naked Domain: ${chalk.cyan(primaryTenant || tenantName)} ${chalk.gray('(primary tenant issuer)')}`);
|
|
1787
|
+
}
|
|
1737
1788
|
}
|
|
1738
1789
|
else {
|
|
1739
|
-
const issuerUrl = config.urls
|
|
1790
|
+
const issuerUrl = config.urls?.api?.custom || config.urls?.api?.auto;
|
|
1740
1791
|
console.log(` Issuer URL: ${chalk.cyan(issuerUrl)}`);
|
|
1741
1792
|
}
|
|
1742
1793
|
console.log(` Default Tenant: ${chalk.cyan(tenantName)}`);
|
|
@@ -1744,8 +1795,11 @@ async function runNormalSetup(options) {
|
|
|
1744
1795
|
console.log('');
|
|
1745
1796
|
// Public URLs
|
|
1746
1797
|
console.log(chalk.bold('Public URLs:'));
|
|
1747
|
-
if (
|
|
1798
|
+
if (baseDomain) {
|
|
1748
1799
|
console.log(` API Router: ${chalk.cyan('*.' + baseDomain)} → ${chalk.gray(envPrefix + '-ar-router')}`);
|
|
1800
|
+
if (nakedDomain) {
|
|
1801
|
+
console.log(` API Router: ${chalk.cyan(baseDomain + ' (naked)')} → ${chalk.gray(envPrefix + '-ar-router')}`);
|
|
1802
|
+
}
|
|
1749
1803
|
}
|
|
1750
1804
|
else {
|
|
1751
1805
|
console.log(` API Router: ${chalk.cyan(config.urls.api.custom || config.urls.api.auto)}`);
|
|
@@ -1968,11 +2022,9 @@ async function executeSetup(config, cfApiToken, keepPath) {
|
|
|
1968
2022
|
// Step 4.5: Generate ui.env for UI builds
|
|
1969
2023
|
const uiEnvSpinner = ora('Generating UI environment file...').start();
|
|
1970
2024
|
try {
|
|
1971
|
-
const
|
|
1972
|
-
if (
|
|
1973
|
-
await saveUiEnv(envPaths.uiEnv,
|
|
1974
|
-
PUBLIC_API_BASE_URL: apiBaseUrl,
|
|
1975
|
-
});
|
|
2025
|
+
const initialUiEnv = buildInitialUiEnvConfig(config);
|
|
2026
|
+
if (initialUiEnv) {
|
|
2027
|
+
await saveUiEnv(envPaths.uiEnv, initialUiEnv);
|
|
1976
2028
|
uiEnvSpinner.succeed(`UI env saved (${envPaths.uiEnv})`);
|
|
1977
2029
|
}
|
|
1978
2030
|
else {
|
|
@@ -2366,7 +2418,7 @@ async function editUrls(config) {
|
|
|
2366
2418
|
console.log('');
|
|
2367
2419
|
const apiDomain = await input({
|
|
2368
2420
|
message: 'API (issuer) domain (leave empty for workers.dev)',
|
|
2369
|
-
default: config.urls.api?.custom
|
|
2421
|
+
default: stripProtocol(config.urls.api?.custom),
|
|
2370
2422
|
validate: (value) => {
|
|
2371
2423
|
if (!value)
|
|
2372
2424
|
return true;
|
|
@@ -2376,28 +2428,36 @@ async function editUrls(config) {
|
|
|
2376
2428
|
return true;
|
|
2377
2429
|
},
|
|
2378
2430
|
});
|
|
2431
|
+
// Check Cloudflare zone for the domain
|
|
2432
|
+
const updateDomainConfig = {};
|
|
2433
|
+
if (apiDomain) {
|
|
2434
|
+
console.log('');
|
|
2435
|
+
try {
|
|
2436
|
+
await checkAndPromptZone(apiDomain, updateDomainConfig);
|
|
2437
|
+
}
|
|
2438
|
+
catch {
|
|
2439
|
+
// User cancelled - non-fatal
|
|
2440
|
+
}
|
|
2441
|
+
console.log('');
|
|
2442
|
+
}
|
|
2379
2443
|
const loginUiDomain = await input({
|
|
2380
2444
|
message: 'Login UI domain (leave empty for pages.dev)',
|
|
2381
|
-
default: config.urls.loginUi?.custom
|
|
2445
|
+
default: stripProtocol(config.urls.loginUi?.custom),
|
|
2382
2446
|
});
|
|
2383
2447
|
const adminUiDomain = await input({
|
|
2384
2448
|
message: 'Admin UI domain (leave empty for pages.dev)',
|
|
2385
|
-
default: config.urls.adminUi?.custom
|
|
2449
|
+
default: stripProtocol(config.urls.adminUi?.custom),
|
|
2450
|
+
});
|
|
2451
|
+
config.urls = buildUrlsConfig({
|
|
2452
|
+
env,
|
|
2453
|
+
apiDomain,
|
|
2454
|
+
loginUiDomain,
|
|
2455
|
+
adminUiDomain,
|
|
2456
|
+
zoneId: updateDomainConfig.zoneId,
|
|
2457
|
+
customDomainBinding: updateDomainConfig.customDomainBinding,
|
|
2458
|
+
workersSubdomain,
|
|
2459
|
+
existingUrls: config.urls,
|
|
2386
2460
|
});
|
|
2387
|
-
config.urls.api = {
|
|
2388
|
-
custom: apiDomain || null,
|
|
2389
|
-
auto: config.urls.api?.auto || getWorkersDevUrl(env + '-ar-router'),
|
|
2390
|
-
};
|
|
2391
|
-
config.urls.loginUi = {
|
|
2392
|
-
custom: loginUiDomain || null,
|
|
2393
|
-
auto: config.urls.loginUi?.auto || getPagesDevUrl(env + '-ar-login-ui'),
|
|
2394
|
-
sameAsApi: config.urls.loginUi?.sameAsApi ?? false,
|
|
2395
|
-
};
|
|
2396
|
-
config.urls.adminUi = {
|
|
2397
|
-
custom: adminUiDomain || null,
|
|
2398
|
-
auto: config.urls.adminUi?.auto || getPagesDevUrl(env + '-ar-admin-ui'),
|
|
2399
|
-
sameAsApi: config.urls.adminUi?.sameAsApi ?? false,
|
|
2400
|
-
};
|
|
2401
2461
|
return true;
|
|
2402
2462
|
}
|
|
2403
2463
|
// =============================================================================
|