@authrim/setup 0.1.49 → 0.1.52
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/cli/commands/init.js +274 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/core/cloudflare.d.ts +16 -4
- package/dist/core/cloudflare.d.ts.map +1 -1
- package/dist/core/cloudflare.js +45 -4
- package/dist/core/cloudflare.js.map +1 -1
- package/dist/core/config.d.ts +225 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +42 -0
- package/dist/core/config.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +101 -4
- package/dist/web/api.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +601 -28
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/web/ui.js
CHANGED
|
@@ -746,6 +746,101 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
746
746
|
color: var(--text-muted);
|
|
747
747
|
font-size: 0.8rem;
|
|
748
748
|
}
|
|
749
|
+
|
|
750
|
+
/* Database configuration styles */
|
|
751
|
+
.database-config-grid {
|
|
752
|
+
display: grid;
|
|
753
|
+
grid-template-columns: 1fr 1fr;
|
|
754
|
+
gap: 1.5rem;
|
|
755
|
+
margin-bottom: 1.5rem;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.database-config-stack {
|
|
759
|
+
display: flex;
|
|
760
|
+
flex-direction: column;
|
|
761
|
+
gap: 1.5rem;
|
|
762
|
+
margin-bottom: 1.5rem;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.database-card {
|
|
766
|
+
background: var(--bg);
|
|
767
|
+
border: 1px solid var(--border);
|
|
768
|
+
border-radius: 0.5rem;
|
|
769
|
+
padding: 1.25rem;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
.database-card h3 {
|
|
773
|
+
margin: 0 0 1rem 0;
|
|
774
|
+
font-size: 1.1rem;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.db-description {
|
|
778
|
+
font-size: 0.875rem;
|
|
779
|
+
color: var(--text-muted);
|
|
780
|
+
margin-bottom: 1rem;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.db-description p {
|
|
784
|
+
margin: 0 0 0.5rem 0;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.db-description ul {
|
|
788
|
+
margin: 0.5rem 0;
|
|
789
|
+
padding-left: 1.25rem;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.db-description li {
|
|
793
|
+
margin-bottom: 0.25rem;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.db-hint {
|
|
797
|
+
font-style: italic;
|
|
798
|
+
margin-top: 0.75rem;
|
|
799
|
+
padding: 0.5rem;
|
|
800
|
+
background: #f0f9ff;
|
|
801
|
+
border-radius: 4px;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.region-selection h4 {
|
|
805
|
+
margin: 0 0 0.75rem 0;
|
|
806
|
+
font-size: 0.95rem;
|
|
807
|
+
font-weight: 600;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.radio-group {
|
|
811
|
+
display: flex;
|
|
812
|
+
flex-direction: column;
|
|
813
|
+
gap: 0.5rem;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.radio-item {
|
|
817
|
+
display: flex;
|
|
818
|
+
align-items: center;
|
|
819
|
+
gap: 0.5rem;
|
|
820
|
+
cursor: pointer;
|
|
821
|
+
padding: 0.25rem 0;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
.radio-item input[type="radio"] {
|
|
825
|
+
margin: 0;
|
|
826
|
+
width: 16px;
|
|
827
|
+
height: 16px;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
.radio-separator {
|
|
831
|
+
font-size: 0.75rem;
|
|
832
|
+
color: var(--text-muted);
|
|
833
|
+
margin: 0.5rem 0 0.25rem 0;
|
|
834
|
+
font-weight: 500;
|
|
835
|
+
text-transform: uppercase;
|
|
836
|
+
letter-spacing: 0.05em;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
@media (max-width: 768px) {
|
|
840
|
+
.database-config-grid {
|
|
841
|
+
grid-template-columns: 1fr;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
749
844
|
</style>
|
|
750
845
|
</head>
|
|
751
846
|
<body>
|
|
@@ -772,6 +867,14 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
772
867
|
<div class="step step-pending" id="step-3">3</div>
|
|
773
868
|
<div class="step-connector"></div>
|
|
774
869
|
<div class="step step-pending" id="step-4">4</div>
|
|
870
|
+
<div class="step-connector"></div>
|
|
871
|
+
<div class="step step-pending" id="step-5">5</div>
|
|
872
|
+
<div class="step-connector"></div>
|
|
873
|
+
<div class="step step-pending" id="step-6">6</div>
|
|
874
|
+
<div class="step-connector"></div>
|
|
875
|
+
<div class="step step-pending" id="step-7">7</div>
|
|
876
|
+
<div class="step-connector"></div>
|
|
877
|
+
<div class="step step-pending" id="step-8">8</div>
|
|
775
878
|
</div>
|
|
776
879
|
|
|
777
880
|
<!-- Step 1: Prerequisites -->
|
|
@@ -1035,7 +1138,210 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1035
1138
|
</div>
|
|
1036
1139
|
</div>
|
|
1037
1140
|
|
|
1038
|
-
<!-- Step 3:
|
|
1141
|
+
<!-- Step 3: Database Configuration -->
|
|
1142
|
+
<div id="section-database" class="card hidden">
|
|
1143
|
+
<h2 class="card-title">🗄️ Database Configuration</h2>
|
|
1144
|
+
|
|
1145
|
+
<p style="margin-bottom: 1rem; color: var(--text-muted);">
|
|
1146
|
+
Authrim uses two separate D1 databases to isolate personal data from application data.
|
|
1147
|
+
</p>
|
|
1148
|
+
|
|
1149
|
+
<div class="warning-box" style="background: #fef3c7; border-left: 4px solid #f59e0b; padding: 0.75rem 1rem; margin-bottom: 1.5rem; border-radius: 0.375rem;">
|
|
1150
|
+
⚠️ Database region cannot be changed after creation.
|
|
1151
|
+
</div>
|
|
1152
|
+
|
|
1153
|
+
<div class="database-config-stack">
|
|
1154
|
+
<!-- Core Database (Non-PII) -->
|
|
1155
|
+
<div class="database-card">
|
|
1156
|
+
<h3>🗄️ Core Database <span style="font-size: 0.8rem; font-weight: normal; color: var(--text-muted);">(Non-PII)</span></h3>
|
|
1157
|
+
<div class="db-description">
|
|
1158
|
+
<p>Stores non-personal application data including:</p>
|
|
1159
|
+
<ul>
|
|
1160
|
+
<li>OAuth clients and their configurations</li>
|
|
1161
|
+
<li>Authorization codes and access tokens</li>
|
|
1162
|
+
<li>User sessions and login state</li>
|
|
1163
|
+
<li>Tenant settings and configurations</li>
|
|
1164
|
+
<li>Audit logs and security events</li>
|
|
1165
|
+
</ul>
|
|
1166
|
+
<p class="db-hint">This database handles all authentication flows and should be placed close to your primary user base.</p>
|
|
1167
|
+
</div>
|
|
1168
|
+
|
|
1169
|
+
<div class="region-selection">
|
|
1170
|
+
<h4>Region</h4>
|
|
1171
|
+
<div class="radio-group">
|
|
1172
|
+
<label class="radio-item">
|
|
1173
|
+
<input type="radio" name="db-core-location" value="auto" checked>
|
|
1174
|
+
<span>Automatic (nearest to you)</span>
|
|
1175
|
+
</label>
|
|
1176
|
+
<div class="radio-separator">Location Hints</div>
|
|
1177
|
+
<label class="radio-item">
|
|
1178
|
+
<input type="radio" name="db-core-location" value="wnam">
|
|
1179
|
+
<span>North America (West)</span>
|
|
1180
|
+
</label>
|
|
1181
|
+
<label class="radio-item">
|
|
1182
|
+
<input type="radio" name="db-core-location" value="enam">
|
|
1183
|
+
<span>North America (East)</span>
|
|
1184
|
+
</label>
|
|
1185
|
+
<label class="radio-item">
|
|
1186
|
+
<input type="radio" name="db-core-location" value="weur">
|
|
1187
|
+
<span>Europe (West)</span>
|
|
1188
|
+
</label>
|
|
1189
|
+
<label class="radio-item">
|
|
1190
|
+
<input type="radio" name="db-core-location" value="eeur">
|
|
1191
|
+
<span>Europe (East)</span>
|
|
1192
|
+
</label>
|
|
1193
|
+
<label class="radio-item">
|
|
1194
|
+
<input type="radio" name="db-core-location" value="apac">
|
|
1195
|
+
<span>Asia Pacific</span>
|
|
1196
|
+
</label>
|
|
1197
|
+
<label class="radio-item">
|
|
1198
|
+
<input type="radio" name="db-core-location" value="oc">
|
|
1199
|
+
<span>Oceania</span>
|
|
1200
|
+
</label>
|
|
1201
|
+
<div class="radio-separator">Jurisdiction (Compliance)</div>
|
|
1202
|
+
<label class="radio-item">
|
|
1203
|
+
<input type="radio" name="db-core-location" value="eu">
|
|
1204
|
+
<span>EU Jurisdiction (GDPR compliance)</span>
|
|
1205
|
+
</label>
|
|
1206
|
+
</div>
|
|
1207
|
+
</div>
|
|
1208
|
+
</div>
|
|
1209
|
+
|
|
1210
|
+
<!-- PII Database -->
|
|
1211
|
+
<div class="database-card">
|
|
1212
|
+
<h3>🔒 PII Database <span style="font-size: 0.8rem; font-weight: normal; color: var(--text-muted);">(Personal Identifiable Information)</span></h3>
|
|
1213
|
+
<div class="db-description">
|
|
1214
|
+
<p>Stores personal user data including:</p>
|
|
1215
|
+
<ul>
|
|
1216
|
+
<li>User profiles (name, email, phone)</li>
|
|
1217
|
+
<li>Passkey/WebAuthn credentials</li>
|
|
1218
|
+
<li>User preferences and settings</li>
|
|
1219
|
+
<li>Any custom user attributes</li>
|
|
1220
|
+
</ul>
|
|
1221
|
+
<p class="db-hint">This database contains personal data. Consider placing it in a region that complies with your data protection requirements.</p>
|
|
1222
|
+
</div>
|
|
1223
|
+
|
|
1224
|
+
<div class="region-selection">
|
|
1225
|
+
<h4>Region</h4>
|
|
1226
|
+
<div class="radio-group">
|
|
1227
|
+
<label class="radio-item">
|
|
1228
|
+
<input type="radio" name="db-pii-location" value="auto" checked>
|
|
1229
|
+
<span>Automatic (nearest to you)</span>
|
|
1230
|
+
</label>
|
|
1231
|
+
<div class="radio-separator">Location Hints</div>
|
|
1232
|
+
<label class="radio-item">
|
|
1233
|
+
<input type="radio" name="db-pii-location" value="wnam">
|
|
1234
|
+
<span>North America (West)</span>
|
|
1235
|
+
</label>
|
|
1236
|
+
<label class="radio-item">
|
|
1237
|
+
<input type="radio" name="db-pii-location" value="enam">
|
|
1238
|
+
<span>North America (East)</span>
|
|
1239
|
+
</label>
|
|
1240
|
+
<label class="radio-item">
|
|
1241
|
+
<input type="radio" name="db-pii-location" value="weur">
|
|
1242
|
+
<span>Europe (West)</span>
|
|
1243
|
+
</label>
|
|
1244
|
+
<label class="radio-item">
|
|
1245
|
+
<input type="radio" name="db-pii-location" value="eeur">
|
|
1246
|
+
<span>Europe (East)</span>
|
|
1247
|
+
</label>
|
|
1248
|
+
<label class="radio-item">
|
|
1249
|
+
<input type="radio" name="db-pii-location" value="apac">
|
|
1250
|
+
<span>Asia Pacific</span>
|
|
1251
|
+
</label>
|
|
1252
|
+
<label class="radio-item">
|
|
1253
|
+
<input type="radio" name="db-pii-location" value="oc">
|
|
1254
|
+
<span>Oceania</span>
|
|
1255
|
+
</label>
|
|
1256
|
+
<div class="radio-separator">Jurisdiction (Compliance)</div>
|
|
1257
|
+
<label class="radio-item">
|
|
1258
|
+
<input type="radio" name="db-pii-location" value="eu">
|
|
1259
|
+
<span>EU Jurisdiction (GDPR compliance)</span>
|
|
1260
|
+
</label>
|
|
1261
|
+
</div>
|
|
1262
|
+
</div>
|
|
1263
|
+
</div>
|
|
1264
|
+
</div>
|
|
1265
|
+
|
|
1266
|
+
<div class="button-group">
|
|
1267
|
+
<button class="btn-secondary" id="btn-back-database">Back</button>
|
|
1268
|
+
<button class="btn-primary" id="btn-continue-database">Continue</button>
|
|
1269
|
+
</div>
|
|
1270
|
+
</div>
|
|
1271
|
+
|
|
1272
|
+
<!-- Step 4: Email Provider Configuration -->
|
|
1273
|
+
<div id="section-email" class="card hidden">
|
|
1274
|
+
<h2 class="card-title">📧 Email Provider</h2>
|
|
1275
|
+
|
|
1276
|
+
<p style="margin-bottom: 1rem; color: var(--text-muted);">
|
|
1277
|
+
Configure email sending for magic links and verification codes.
|
|
1278
|
+
You can configure this later if you prefer.
|
|
1279
|
+
</p>
|
|
1280
|
+
|
|
1281
|
+
<div class="radio-group" style="margin-bottom: 1.5rem;">
|
|
1282
|
+
<label class="radio-item" style="padding: 0.75rem; border: 1px solid var(--border); border-radius: 8px;">
|
|
1283
|
+
<input type="radio" name="email-setup-choice" value="later" checked>
|
|
1284
|
+
<span style="display: flex; flex-direction: column; gap: 0.25rem;">
|
|
1285
|
+
<strong>Configure later</strong>
|
|
1286
|
+
<small style="color: var(--text-muted);">Skip for now. Magic links will return URLs instead of sending emails.</small>
|
|
1287
|
+
</span>
|
|
1288
|
+
</label>
|
|
1289
|
+
<label class="radio-item" style="padding: 0.75rem; border: 1px solid var(--border); border-radius: 8px; margin-top: 0.5rem;">
|
|
1290
|
+
<input type="radio" name="email-setup-choice" value="configure">
|
|
1291
|
+
<span style="display: flex; flex-direction: column; gap: 0.25rem;">
|
|
1292
|
+
<strong>Configure Resend</strong>
|
|
1293
|
+
<small style="color: var(--text-muted);">Set up email sending with Resend (recommended for production).</small>
|
|
1294
|
+
</span>
|
|
1295
|
+
</label>
|
|
1296
|
+
</div>
|
|
1297
|
+
|
|
1298
|
+
<!-- Resend Configuration Form (hidden by default) -->
|
|
1299
|
+
<div id="resend-config-form" class="hidden" style="background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem;">
|
|
1300
|
+
<h3 style="margin: 0 0 1rem 0; font-size: 1rem;">🔑 Resend Configuration</h3>
|
|
1301
|
+
|
|
1302
|
+
<div class="alert alert-info" style="margin-bottom: 1rem;">
|
|
1303
|
+
<strong>📋 Before you begin:</strong>
|
|
1304
|
+
<ol style="margin: 0.5rem 0 0 1rem; padding: 0;">
|
|
1305
|
+
<li>Create a Resend account at <a href="https://resend.com" target="_blank" style="color: var(--primary);">resend.com</a></li>
|
|
1306
|
+
<li>Add and verify your domain at <a href="https://resend.com/domains" target="_blank" style="color: var(--primary);">Domains Dashboard</a></li>
|
|
1307
|
+
<li>Create an API key at <a href="https://resend.com/api-keys" target="_blank" style="color: var(--primary);">API Keys</a></li>
|
|
1308
|
+
</ol>
|
|
1309
|
+
</div>
|
|
1310
|
+
|
|
1311
|
+
<div class="form-group">
|
|
1312
|
+
<label for="resend-api-key">Resend API Key</label>
|
|
1313
|
+
<input type="password" id="resend-api-key" placeholder="re_xxxxxxxxxx" autocomplete="off">
|
|
1314
|
+
<small style="color: var(--text-muted);">Your API key starts with "re_"</small>
|
|
1315
|
+
</div>
|
|
1316
|
+
|
|
1317
|
+
<div class="form-group">
|
|
1318
|
+
<label for="email-from-address">From Email Address</label>
|
|
1319
|
+
<input type="email" id="email-from-address" placeholder="noreply@yourdomain.com" autocomplete="off">
|
|
1320
|
+
<small style="color: var(--text-muted);">Must be from a verified domain in your Resend account</small>
|
|
1321
|
+
</div>
|
|
1322
|
+
|
|
1323
|
+
<div class="form-group">
|
|
1324
|
+
<label for="email-from-name">From Display Name (optional)</label>
|
|
1325
|
+
<input type="text" id="email-from-name" placeholder="Authrim" autocomplete="off">
|
|
1326
|
+
<small style="color: var(--text-muted);">Displayed as the sender name in email clients</small>
|
|
1327
|
+
</div>
|
|
1328
|
+
|
|
1329
|
+
<div class="alert alert-warning" style="margin-top: 1rem;">
|
|
1330
|
+
<strong>⚠️ Domain Verification Required</strong>
|
|
1331
|
+
<p style="margin: 0.25rem 0 0 0; font-size: 0.875rem;">
|
|
1332
|
+
Before your domain is verified, emails can only be sent from <code>onboarding@resend.dev</code> (for testing).
|
|
1333
|
+
<a href="https://resend.com/docs/dashboard/domains/introduction" target="_blank" style="color: var(--primary);">Learn more about domain verification →</a>
|
|
1334
|
+
</p>
|
|
1335
|
+
</div>
|
|
1336
|
+
</div>
|
|
1337
|
+
|
|
1338
|
+
<div class="button-group">
|
|
1339
|
+
<button class="btn-secondary" id="btn-back-email">Back</button>
|
|
1340
|
+
<button class="btn-primary" id="btn-continue-email">Continue</button>
|
|
1341
|
+
</div>
|
|
1342
|
+
</div>
|
|
1343
|
+
|
|
1344
|
+
<!-- Step 5: Provisioning -->
|
|
1039
1345
|
<div id="section-provision" class="card hidden">
|
|
1040
1346
|
<h2 class="card-title">
|
|
1041
1347
|
Resource Provisioning
|
|
@@ -1307,6 +1613,10 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1307
1613
|
2: document.getElementById('step-2'),
|
|
1308
1614
|
3: document.getElementById('step-3'),
|
|
1309
1615
|
4: document.getElementById('step-4'),
|
|
1616
|
+
5: document.getElementById('step-5'),
|
|
1617
|
+
6: document.getElementById('step-6'),
|
|
1618
|
+
7: document.getElementById('step-7'),
|
|
1619
|
+
8: document.getElementById('step-8'),
|
|
1310
1620
|
};
|
|
1311
1621
|
|
|
1312
1622
|
const sections = {
|
|
@@ -1315,6 +1625,8 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1315
1625
|
mode: document.getElementById('section-mode'),
|
|
1316
1626
|
loadConfig: document.getElementById('section-load-config'),
|
|
1317
1627
|
config: document.getElementById('section-config'),
|
|
1628
|
+
database: document.getElementById('section-database'),
|
|
1629
|
+
email: document.getElementById('section-email'),
|
|
1318
1630
|
provision: document.getElementById('section-provision'),
|
|
1319
1631
|
deploy: document.getElementById('section-deploy'),
|
|
1320
1632
|
complete: document.getElementById('section-complete'),
|
|
@@ -1346,7 +1658,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1346
1658
|
// Step navigation
|
|
1347
1659
|
function setStep(step) {
|
|
1348
1660
|
currentStep = step;
|
|
1349
|
-
for (let i = 1; i <=
|
|
1661
|
+
for (let i = 1; i <= 7; i++) {
|
|
1350
1662
|
const el = steps[i];
|
|
1351
1663
|
el.className = 'step ' + (i < step ? 'step-complete' : i === step ? 'step-active' : 'step-pending');
|
|
1352
1664
|
}
|
|
@@ -1577,28 +1889,80 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1577
1889
|
document.getElementById('btn-load-config').addEventListener('click', async () => {
|
|
1578
1890
|
if (!loadedConfig) return;
|
|
1579
1891
|
|
|
1580
|
-
//
|
|
1892
|
+
// Support both new format (v1.0.0) and old format (v0.1.x)
|
|
1893
|
+
const isNewFormat = loadedConfig.version === '1.0.0' || loadedConfig.environment?.prefix;
|
|
1894
|
+
|
|
1895
|
+
// Extract values (with fallback for old format)
|
|
1896
|
+
const env = isNewFormat
|
|
1897
|
+
? loadedConfig.environment?.prefix
|
|
1898
|
+
: loadedConfig.env || 'prod';
|
|
1899
|
+
|
|
1900
|
+
const apiDomain = isNewFormat
|
|
1901
|
+
? loadedConfig.urls?.api?.custom
|
|
1902
|
+
: loadedConfig.apiDomain;
|
|
1903
|
+
|
|
1904
|
+
const loginUiDomain = isNewFormat
|
|
1905
|
+
? loadedConfig.urls?.loginUi?.custom
|
|
1906
|
+
: loadedConfig.loginUiDomain;
|
|
1907
|
+
|
|
1908
|
+
const adminUiDomain = isNewFormat
|
|
1909
|
+
? loadedConfig.urls?.adminUi?.custom
|
|
1910
|
+
: loadedConfig.adminUiDomain;
|
|
1911
|
+
|
|
1912
|
+
const tenant = loadedConfig.tenant || {
|
|
1913
|
+
name: 'default',
|
|
1914
|
+
displayName: 'Default Tenant',
|
|
1915
|
+
multiTenant: false,
|
|
1916
|
+
};
|
|
1917
|
+
|
|
1918
|
+
const components = loadedConfig.components || {
|
|
1919
|
+
api: true,
|
|
1920
|
+
loginUi: true,
|
|
1921
|
+
adminUi: true,
|
|
1922
|
+
saml: false,
|
|
1923
|
+
async: false,
|
|
1924
|
+
vc: false,
|
|
1925
|
+
bridge: true,
|
|
1926
|
+
policy: true,
|
|
1927
|
+
};
|
|
1928
|
+
|
|
1929
|
+
// Build internal config
|
|
1581
1930
|
config = {
|
|
1582
|
-
env
|
|
1583
|
-
apiDomain:
|
|
1584
|
-
loginUiDomain:
|
|
1585
|
-
adminUiDomain:
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
loginUi: true,
|
|
1589
|
-
adminUi: true,
|
|
1590
|
-
saml: false,
|
|
1591
|
-
async: false,
|
|
1592
|
-
vc: false,
|
|
1593
|
-
},
|
|
1931
|
+
env,
|
|
1932
|
+
apiDomain: apiDomain || null,
|
|
1933
|
+
loginUiDomain: loginUiDomain || null,
|
|
1934
|
+
adminUiDomain: adminUiDomain || null,
|
|
1935
|
+
tenant,
|
|
1936
|
+
components,
|
|
1594
1937
|
};
|
|
1595
1938
|
|
|
1596
1939
|
// Set form values
|
|
1597
1940
|
document.getElementById('env').value = config.env;
|
|
1598
|
-
document.getElementById('
|
|
1941
|
+
document.getElementById('base-domain').value = config.tenant?.baseDomain || config.apiDomain || '';
|
|
1599
1942
|
document.getElementById('login-domain').value = config.loginUiDomain || '';
|
|
1600
1943
|
document.getElementById('admin-domain').value = config.adminUiDomain || '';
|
|
1601
|
-
|
|
1944
|
+
document.getElementById('tenant-name').value = config.tenant?.name || 'default';
|
|
1945
|
+
document.getElementById('tenant-display').value = config.tenant?.displayName || 'Default Tenant';
|
|
1946
|
+
document.getElementById('naked-domain').checked = config.tenant?.nakedDomain || false;
|
|
1947
|
+
|
|
1948
|
+
// Set component checkboxes
|
|
1949
|
+
if (document.getElementById('comp-login-ui')) {
|
|
1950
|
+
document.getElementById('comp-login-ui').checked = components.loginUi !== false;
|
|
1951
|
+
}
|
|
1952
|
+
if (document.getElementById('comp-admin-ui')) {
|
|
1953
|
+
document.getElementById('comp-admin-ui').checked = components.adminUi !== false;
|
|
1954
|
+
}
|
|
1955
|
+
if (document.getElementById('comp-saml')) {
|
|
1956
|
+
document.getElementById('comp-saml').checked = components.saml === true;
|
|
1957
|
+
}
|
|
1958
|
+
if (document.getElementById('comp-async')) {
|
|
1959
|
+
document.getElementById('comp-async').checked = components.async === true;
|
|
1960
|
+
}
|
|
1961
|
+
if (document.getElementById('comp-vc')) {
|
|
1962
|
+
document.getElementById('comp-vc').checked = components.vc === true;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
// Trigger env input to update preview/default labels
|
|
1602
1966
|
document.getElementById('env').dispatchEvent(new Event('input'));
|
|
1603
1967
|
|
|
1604
1968
|
// Skip to provisioning if resources already exist
|
|
@@ -1873,15 +2237,145 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1873
2237
|
updateResourcePreview(env);
|
|
1874
2238
|
updateProvisionButtons();
|
|
1875
2239
|
|
|
1876
|
-
|
|
1877
|
-
|
|
2240
|
+
// Go to database configuration step
|
|
2241
|
+
setStep(4);
|
|
2242
|
+
showSection('database');
|
|
1878
2243
|
});
|
|
1879
2244
|
|
|
1880
2245
|
document.getElementById('btn-back-config').addEventListener('click', () => {
|
|
1881
|
-
|
|
2246
|
+
// Go back to email configuration (previous step in the flow)
|
|
2247
|
+
setStep(5);
|
|
2248
|
+
showSection('email');
|
|
2249
|
+
});
|
|
2250
|
+
|
|
2251
|
+
// Database configuration handlers
|
|
2252
|
+
document.getElementById('btn-back-database').addEventListener('click', () => {
|
|
2253
|
+
setStep(3);
|
|
1882
2254
|
showSection('config');
|
|
1883
2255
|
});
|
|
1884
2256
|
|
|
2257
|
+
document.getElementById('btn-continue-database').addEventListener('click', () => {
|
|
2258
|
+
// Get selected values
|
|
2259
|
+
const coreLocation = document.querySelector('input[name="db-core-location"]:checked').value;
|
|
2260
|
+
const piiLocation = document.querySelector('input[name="db-pii-location"]:checked').value;
|
|
2261
|
+
|
|
2262
|
+
// Parse location vs jurisdiction
|
|
2263
|
+
function parseDbLocation(value) {
|
|
2264
|
+
if (value === 'eu') {
|
|
2265
|
+
return { location: 'auto', jurisdiction: 'eu' };
|
|
2266
|
+
}
|
|
2267
|
+
return { location: value, jurisdiction: 'none' };
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
// Add database config to config object
|
|
2271
|
+
config.database = {
|
|
2272
|
+
core: parseDbLocation(coreLocation),
|
|
2273
|
+
pii: parseDbLocation(piiLocation),
|
|
2274
|
+
};
|
|
2275
|
+
|
|
2276
|
+
// Proceed to email configuration
|
|
2277
|
+
setStep(5);
|
|
2278
|
+
showSection('email');
|
|
2279
|
+
});
|
|
2280
|
+
|
|
2281
|
+
// Email configuration handlers
|
|
2282
|
+
// Toggle resend config form visibility
|
|
2283
|
+
document.querySelectorAll('input[name="email-setup-choice"]').forEach(radio => {
|
|
2284
|
+
radio.addEventListener('change', () => {
|
|
2285
|
+
const resendForm = document.getElementById('resend-config-form');
|
|
2286
|
+
const choice = document.querySelector('input[name="email-setup-choice"]:checked').value;
|
|
2287
|
+
if (choice === 'configure') {
|
|
2288
|
+
resendForm.classList.remove('hidden');
|
|
2289
|
+
} else {
|
|
2290
|
+
resendForm.classList.add('hidden');
|
|
2291
|
+
}
|
|
2292
|
+
});
|
|
2293
|
+
});
|
|
2294
|
+
|
|
2295
|
+
document.getElementById('btn-back-email').addEventListener('click', () => {
|
|
2296
|
+
setStep(4);
|
|
2297
|
+
showSection('database');
|
|
2298
|
+
});
|
|
2299
|
+
|
|
2300
|
+
document.getElementById('btn-continue-email').addEventListener('click', async () => {
|
|
2301
|
+
const choice = document.querySelector('input[name="email-setup-choice"]:checked').value;
|
|
2302
|
+
const btn = document.getElementById('btn-continue-email');
|
|
2303
|
+
|
|
2304
|
+
if (choice === 'configure') {
|
|
2305
|
+
// Validate and store email configuration
|
|
2306
|
+
const apiKey = document.getElementById('resend-api-key').value.trim();
|
|
2307
|
+
const fromAddress = document.getElementById('email-from-address').value.trim();
|
|
2308
|
+
const fromName = document.getElementById('email-from-name').value.trim();
|
|
2309
|
+
|
|
2310
|
+
// Validate API key format
|
|
2311
|
+
if (!apiKey) {
|
|
2312
|
+
alert('Please enter your Resend API key');
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
if (!apiKey.startsWith('re_')) {
|
|
2316
|
+
if (!confirm('API key does not start with "re_". This may not be a valid Resend API key. Continue anyway?')) {
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
// Validate email address
|
|
2322
|
+
if (!fromAddress) {
|
|
2323
|
+
alert('Please enter a From email address');
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
if (!fromAddress.includes('@')) {
|
|
2327
|
+
alert('Please enter a valid email address');
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// Save email configuration to server
|
|
2332
|
+
btn.disabled = true;
|
|
2333
|
+
btn.textContent = 'Saving...';
|
|
2334
|
+
|
|
2335
|
+
try {
|
|
2336
|
+
const result = await api('/email/configure', {
|
|
2337
|
+
method: 'POST',
|
|
2338
|
+
body: {
|
|
2339
|
+
env: config.env,
|
|
2340
|
+
provider: 'resend',
|
|
2341
|
+
apiKey: apiKey,
|
|
2342
|
+
fromAddress: fromAddress,
|
|
2343
|
+
fromName: fromName || undefined,
|
|
2344
|
+
},
|
|
2345
|
+
});
|
|
2346
|
+
|
|
2347
|
+
if (!result.success) {
|
|
2348
|
+
throw new Error(result.error || 'Failed to save email configuration');
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
// Store email configuration (without apiKey for config file)
|
|
2352
|
+
config.email = {
|
|
2353
|
+
provider: 'resend',
|
|
2354
|
+
fromAddress: fromAddress,
|
|
2355
|
+
fromName: fromName || undefined,
|
|
2356
|
+
configured: true,
|
|
2357
|
+
};
|
|
2358
|
+
} catch (error) {
|
|
2359
|
+
alert('Failed to save email configuration: ' + error.message);
|
|
2360
|
+
btn.disabled = false;
|
|
2361
|
+
btn.textContent = 'Continue';
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
btn.disabled = false;
|
|
2366
|
+
btn.textContent = 'Continue';
|
|
2367
|
+
} else {
|
|
2368
|
+
// Configure later - no email provider
|
|
2369
|
+
config.email = {
|
|
2370
|
+
provider: 'none',
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
// Proceed to provision
|
|
2375
|
+
setStep(6);
|
|
2376
|
+
showSection('provision');
|
|
2377
|
+
});
|
|
2378
|
+
|
|
1885
2379
|
// Provision
|
|
1886
2380
|
document.getElementById('btn-provision').addEventListener('click', async () => {
|
|
1887
2381
|
const btn = document.getElementById('btn-provision');
|
|
@@ -1953,7 +2447,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1953
2447
|
|
|
1954
2448
|
const result = await api('/provision', {
|
|
1955
2449
|
method: 'POST',
|
|
1956
|
-
body: { env: config.env },
|
|
2450
|
+
body: { env: config.env, databaseConfig: config.database },
|
|
1957
2451
|
});
|
|
1958
2452
|
|
|
1959
2453
|
// Stop polling
|
|
@@ -1999,12 +2493,12 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1999
2493
|
|
|
2000
2494
|
// Continue to Deploy button
|
|
2001
2495
|
document.getElementById('btn-goto-deploy').addEventListener('click', () => {
|
|
2002
|
-
setStep(
|
|
2496
|
+
setStep(7);
|
|
2003
2497
|
showSection('deploy');
|
|
2004
2498
|
});
|
|
2005
2499
|
|
|
2006
2500
|
document.getElementById('btn-back-provision').addEventListener('click', () => {
|
|
2007
|
-
setStep(
|
|
2501
|
+
setStep(6);
|
|
2008
2502
|
// Update buttons based on provisioning status
|
|
2009
2503
|
updateProvisionButtons();
|
|
2010
2504
|
// Show resource preview if not completed
|
|
@@ -2211,6 +2705,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
2211
2705
|
urlsEl.appendChild(debugDiv);
|
|
2212
2706
|
}
|
|
2213
2707
|
|
|
2708
|
+
setStep(8);
|
|
2214
2709
|
showSection('complete');
|
|
2215
2710
|
}
|
|
2216
2711
|
|
|
@@ -2293,19 +2788,97 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
2293
2788
|
}
|
|
2294
2789
|
}
|
|
2295
2790
|
|
|
2296
|
-
// Save configuration to file
|
|
2791
|
+
// Save configuration to file (AuthrimConfigSchema format)
|
|
2297
2792
|
function saveConfigToFile() {
|
|
2298
|
-
if (!config) {
|
|
2793
|
+
if (!config || !config.env) {
|
|
2299
2794
|
alert('No configuration to save');
|
|
2300
2795
|
return;
|
|
2301
2796
|
}
|
|
2302
2797
|
|
|
2798
|
+
const now = new Date().toISOString();
|
|
2799
|
+
const env = config.env;
|
|
2800
|
+
|
|
2801
|
+
// Calculate auto-generated URLs
|
|
2802
|
+
const workersDomain = env + '-ar-router.workers.dev';
|
|
2803
|
+
const pagesDomain = env + '-ar-ui.pages.dev';
|
|
2804
|
+
|
|
2805
|
+
// Build issuer URL based on tenant settings
|
|
2806
|
+
let issuerAutoUrl = 'https://' + workersDomain;
|
|
2807
|
+
if (config.tenant && config.tenant.baseDomain) {
|
|
2808
|
+
if (config.tenant.nakedDomain) {
|
|
2809
|
+
issuerAutoUrl = 'https://' + config.tenant.baseDomain;
|
|
2810
|
+
} else {
|
|
2811
|
+
const tenantName = config.tenant.name || 'default';
|
|
2812
|
+
issuerAutoUrl = 'https://' + tenantName + '.' + config.tenant.baseDomain;
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
// Build config in AuthrimConfigSchema format
|
|
2303
2817
|
const configToSave = {
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2818
|
+
version: '1.0.0',
|
|
2819
|
+
createdAt: now,
|
|
2820
|
+
updatedAt: now,
|
|
2821
|
+
environment: {
|
|
2822
|
+
prefix: env,
|
|
2823
|
+
},
|
|
2824
|
+
urls: {
|
|
2825
|
+
api: {
|
|
2826
|
+
custom: config.apiDomain || null,
|
|
2827
|
+
auto: config.apiDomain ? issuerAutoUrl : 'https://' + workersDomain,
|
|
2828
|
+
},
|
|
2829
|
+
loginUi: {
|
|
2830
|
+
custom: config.loginUiDomain || null,
|
|
2831
|
+
auto: 'https://' + pagesDomain,
|
|
2832
|
+
},
|
|
2833
|
+
adminUi: {
|
|
2834
|
+
custom: config.adminUiDomain || null,
|
|
2835
|
+
auto: 'https://' + pagesDomain + '/admin',
|
|
2836
|
+
},
|
|
2837
|
+
},
|
|
2838
|
+
tenant: {
|
|
2839
|
+
name: config.tenant?.name || 'default',
|
|
2840
|
+
displayName: config.tenant?.displayName || 'Default Tenant',
|
|
2841
|
+
multiTenant: config.tenant?.multiTenant || false,
|
|
2842
|
+
baseDomain: config.tenant?.baseDomain || undefined,
|
|
2843
|
+
},
|
|
2844
|
+
components: config.components || {
|
|
2845
|
+
api: true,
|
|
2846
|
+
loginUi: true,
|
|
2847
|
+
adminUi: true,
|
|
2848
|
+
saml: false,
|
|
2849
|
+
async: false,
|
|
2850
|
+
vc: false,
|
|
2851
|
+
bridge: true,
|
|
2852
|
+
policy: true,
|
|
2853
|
+
},
|
|
2854
|
+
keys: {
|
|
2855
|
+
secretsPath: './.keys/',
|
|
2856
|
+
},
|
|
2857
|
+
database: config.database || {
|
|
2858
|
+
core: { location: 'auto', jurisdiction: 'none' },
|
|
2859
|
+
pii: { location: 'auto', jurisdiction: 'none' },
|
|
2860
|
+
},
|
|
2861
|
+
features: {
|
|
2862
|
+
email: {
|
|
2863
|
+
provider: config.email?.provider || 'none',
|
|
2864
|
+
fromAddress: config.email?.fromAddress || undefined,
|
|
2865
|
+
fromName: config.email?.fromName || undefined,
|
|
2866
|
+
configured: config.email?.provider === 'resend' && config.email?.apiKey ? true : false,
|
|
2867
|
+
},
|
|
2868
|
+
},
|
|
2307
2869
|
};
|
|
2308
2870
|
|
|
2871
|
+
// Remove undefined values for cleaner output
|
|
2872
|
+
if (!configToSave.tenant.baseDomain) {
|
|
2873
|
+
delete configToSave.tenant.baseDomain;
|
|
2874
|
+
}
|
|
2875
|
+
if (!configToSave.features.email.fromAddress) {
|
|
2876
|
+
delete configToSave.features.email.fromAddress;
|
|
2877
|
+
}
|
|
2878
|
+
if (!configToSave.features.email.fromName) {
|
|
2879
|
+
delete configToSave.features.email.fromName;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2309
2882
|
const blob = new Blob([JSON.stringify(configToSave, null, 2)], { type: 'application/json' });
|
|
2310
2883
|
const url = URL.createObjectURL(blob);
|
|
2311
2884
|
const a = document.createElement('a');
|