@authrim/setup 0.1.47 → 0.1.51
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 +4 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +280 -8
- 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 +605 -29
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/web/ui.js
CHANGED
|
@@ -746,6 +746,94 @@ 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-card {
|
|
759
|
+
background: var(--bg);
|
|
760
|
+
border: 1px solid var(--border);
|
|
761
|
+
border-radius: 0.5rem;
|
|
762
|
+
padding: 1.25rem;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.database-card h3 {
|
|
766
|
+
margin: 0 0 1rem 0;
|
|
767
|
+
font-size: 1.1rem;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.db-description {
|
|
771
|
+
font-size: 0.875rem;
|
|
772
|
+
color: var(--text-muted);
|
|
773
|
+
margin-bottom: 1rem;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.db-description p {
|
|
777
|
+
margin: 0 0 0.5rem 0;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.db-description ul {
|
|
781
|
+
margin: 0.5rem 0;
|
|
782
|
+
padding-left: 1.25rem;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.db-description li {
|
|
786
|
+
margin-bottom: 0.25rem;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.db-hint {
|
|
790
|
+
font-style: italic;
|
|
791
|
+
margin-top: 0.75rem;
|
|
792
|
+
padding: 0.5rem;
|
|
793
|
+
background: #f0f9ff;
|
|
794
|
+
border-radius: 4px;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.region-selection h4 {
|
|
798
|
+
margin: 0 0 0.75rem 0;
|
|
799
|
+
font-size: 0.95rem;
|
|
800
|
+
font-weight: 600;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.radio-group {
|
|
804
|
+
display: flex;
|
|
805
|
+
flex-direction: column;
|
|
806
|
+
gap: 0.5rem;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.radio-item {
|
|
810
|
+
display: flex;
|
|
811
|
+
align-items: center;
|
|
812
|
+
gap: 0.5rem;
|
|
813
|
+
cursor: pointer;
|
|
814
|
+
padding: 0.25rem 0;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.radio-item input[type="radio"] {
|
|
818
|
+
margin: 0;
|
|
819
|
+
width: 16px;
|
|
820
|
+
height: 16px;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.radio-separator {
|
|
824
|
+
font-size: 0.75rem;
|
|
825
|
+
color: var(--text-muted);
|
|
826
|
+
margin: 0.5rem 0 0.25rem 0;
|
|
827
|
+
font-weight: 500;
|
|
828
|
+
text-transform: uppercase;
|
|
829
|
+
letter-spacing: 0.05em;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
@media (max-width: 768px) {
|
|
833
|
+
.database-config-grid {
|
|
834
|
+
grid-template-columns: 1fr;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
749
837
|
</style>
|
|
750
838
|
</head>
|
|
751
839
|
<body>
|
|
@@ -755,6 +843,15 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
755
843
|
<p class="subtitle">OIDC Provider on Cloudflare Workers</p>
|
|
756
844
|
</header>
|
|
757
845
|
|
|
846
|
+
<!-- Development Warning Banner -->
|
|
847
|
+
<div style="background: #fef3c7; border: 2px solid #f59e0b; border-radius: 0.5rem; padding: 1rem; margin-bottom: 1.5rem; text-align: center;">
|
|
848
|
+
<strong style="color: #92400e;">⚠️ WARNING: Under Development!</strong>
|
|
849
|
+
<p style="color: #78350f; margin: 0.5rem 0 0 0; font-size: 0.875rem;">
|
|
850
|
+
This project is still under active development and does not work correctly yet.<br>
|
|
851
|
+
Admin UI is incomplete and does not support login functionality.
|
|
852
|
+
</p>
|
|
853
|
+
</div>
|
|
854
|
+
|
|
758
855
|
<div class="step-indicator" id="step-indicator">
|
|
759
856
|
<div class="step step-active" id="step-1">1</div>
|
|
760
857
|
<div class="step-connector"></div>
|
|
@@ -763,6 +860,14 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
763
860
|
<div class="step step-pending" id="step-3">3</div>
|
|
764
861
|
<div class="step-connector"></div>
|
|
765
862
|
<div class="step step-pending" id="step-4">4</div>
|
|
863
|
+
<div class="step-connector"></div>
|
|
864
|
+
<div class="step step-pending" id="step-5">5</div>
|
|
865
|
+
<div class="step-connector"></div>
|
|
866
|
+
<div class="step step-pending" id="step-6">6</div>
|
|
867
|
+
<div class="step-connector"></div>
|
|
868
|
+
<div class="step step-pending" id="step-7">7</div>
|
|
869
|
+
<div class="step-connector"></div>
|
|
870
|
+
<div class="step step-pending" id="step-8">8</div>
|
|
766
871
|
</div>
|
|
767
872
|
|
|
768
873
|
<!-- Step 1: Prerequisites -->
|
|
@@ -1026,7 +1131,206 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1026
1131
|
</div>
|
|
1027
1132
|
</div>
|
|
1028
1133
|
|
|
1029
|
-
<!-- Step 3:
|
|
1134
|
+
<!-- Step 3: Database Configuration -->
|
|
1135
|
+
<div id="section-database" class="card hidden">
|
|
1136
|
+
<h2 class="card-title">🗄️ Database Configuration</h2>
|
|
1137
|
+
|
|
1138
|
+
<div class="warning-box" style="background: #fef3c7; border-left: 4px solid #f59e0b; padding: 0.75rem 1rem; margin-bottom: 1.5rem; border-radius: 0.375rem;">
|
|
1139
|
+
⚠️ Database region cannot be changed after creation.
|
|
1140
|
+
</div>
|
|
1141
|
+
|
|
1142
|
+
<div class="database-config-grid">
|
|
1143
|
+
<!-- Core Database -->
|
|
1144
|
+
<div class="database-card">
|
|
1145
|
+
<h3>🗄️ Core Database</h3>
|
|
1146
|
+
<div class="db-description">
|
|
1147
|
+
<p>Stores application data including:</p>
|
|
1148
|
+
<ul>
|
|
1149
|
+
<li>OAuth clients and their configurations</li>
|
|
1150
|
+
<li>Authorization codes and access tokens</li>
|
|
1151
|
+
<li>User sessions and login state</li>
|
|
1152
|
+
<li>Tenant settings and configurations</li>
|
|
1153
|
+
<li>Audit logs and security events</li>
|
|
1154
|
+
</ul>
|
|
1155
|
+
<p class="db-hint">This database handles all authentication flows and should be placed close to your primary user base.</p>
|
|
1156
|
+
</div>
|
|
1157
|
+
|
|
1158
|
+
<div class="region-selection">
|
|
1159
|
+
<h4>Region</h4>
|
|
1160
|
+
<div class="radio-group">
|
|
1161
|
+
<label class="radio-item">
|
|
1162
|
+
<input type="radio" name="db-core-location" value="auto" checked>
|
|
1163
|
+
<span>Automatic (nearest to you)</span>
|
|
1164
|
+
</label>
|
|
1165
|
+
<div class="radio-separator">Location Hints</div>
|
|
1166
|
+
<label class="radio-item">
|
|
1167
|
+
<input type="radio" name="db-core-location" value="wnam">
|
|
1168
|
+
<span>North America (West)</span>
|
|
1169
|
+
</label>
|
|
1170
|
+
<label class="radio-item">
|
|
1171
|
+
<input type="radio" name="db-core-location" value="enam">
|
|
1172
|
+
<span>North America (East)</span>
|
|
1173
|
+
</label>
|
|
1174
|
+
<label class="radio-item">
|
|
1175
|
+
<input type="radio" name="db-core-location" value="weur">
|
|
1176
|
+
<span>Europe (West)</span>
|
|
1177
|
+
</label>
|
|
1178
|
+
<label class="radio-item">
|
|
1179
|
+
<input type="radio" name="db-core-location" value="eeur">
|
|
1180
|
+
<span>Europe (East)</span>
|
|
1181
|
+
</label>
|
|
1182
|
+
<label class="radio-item">
|
|
1183
|
+
<input type="radio" name="db-core-location" value="apac">
|
|
1184
|
+
<span>Asia Pacific</span>
|
|
1185
|
+
</label>
|
|
1186
|
+
<label class="radio-item">
|
|
1187
|
+
<input type="radio" name="db-core-location" value="oc">
|
|
1188
|
+
<span>Oceania</span>
|
|
1189
|
+
</label>
|
|
1190
|
+
<div class="radio-separator">Jurisdiction (Compliance)</div>
|
|
1191
|
+
<label class="radio-item">
|
|
1192
|
+
<input type="radio" name="db-core-location" value="eu">
|
|
1193
|
+
<span>EU Jurisdiction (GDPR compliance)</span>
|
|
1194
|
+
</label>
|
|
1195
|
+
</div>
|
|
1196
|
+
</div>
|
|
1197
|
+
</div>
|
|
1198
|
+
|
|
1199
|
+
<!-- PII Database -->
|
|
1200
|
+
<div class="database-card">
|
|
1201
|
+
<h3>🔒 PII Database</h3>
|
|
1202
|
+
<div class="db-description">
|
|
1203
|
+
<p>Stores personal user data including:</p>
|
|
1204
|
+
<ul>
|
|
1205
|
+
<li>User profiles (name, email, phone)</li>
|
|
1206
|
+
<li>Passkey/WebAuthn credentials</li>
|
|
1207
|
+
<li>User preferences and settings</li>
|
|
1208
|
+
<li>Any custom user attributes</li>
|
|
1209
|
+
</ul>
|
|
1210
|
+
<p class="db-hint">This database contains personal data. Consider placing it in a region that complies with your data protection requirements.</p>
|
|
1211
|
+
</div>
|
|
1212
|
+
|
|
1213
|
+
<div class="region-selection">
|
|
1214
|
+
<h4>Region</h4>
|
|
1215
|
+
<div class="radio-group">
|
|
1216
|
+
<label class="radio-item">
|
|
1217
|
+
<input type="radio" name="db-pii-location" value="auto" checked>
|
|
1218
|
+
<span>Automatic (nearest to you)</span>
|
|
1219
|
+
</label>
|
|
1220
|
+
<div class="radio-separator">Location Hints</div>
|
|
1221
|
+
<label class="radio-item">
|
|
1222
|
+
<input type="radio" name="db-pii-location" value="wnam">
|
|
1223
|
+
<span>North America (West)</span>
|
|
1224
|
+
</label>
|
|
1225
|
+
<label class="radio-item">
|
|
1226
|
+
<input type="radio" name="db-pii-location" value="enam">
|
|
1227
|
+
<span>North America (East)</span>
|
|
1228
|
+
</label>
|
|
1229
|
+
<label class="radio-item">
|
|
1230
|
+
<input type="radio" name="db-pii-location" value="weur">
|
|
1231
|
+
<span>Europe (West)</span>
|
|
1232
|
+
</label>
|
|
1233
|
+
<label class="radio-item">
|
|
1234
|
+
<input type="radio" name="db-pii-location" value="eeur">
|
|
1235
|
+
<span>Europe (East)</span>
|
|
1236
|
+
</label>
|
|
1237
|
+
<label class="radio-item">
|
|
1238
|
+
<input type="radio" name="db-pii-location" value="apac">
|
|
1239
|
+
<span>Asia Pacific</span>
|
|
1240
|
+
</label>
|
|
1241
|
+
<label class="radio-item">
|
|
1242
|
+
<input type="radio" name="db-pii-location" value="oc">
|
|
1243
|
+
<span>Oceania</span>
|
|
1244
|
+
</label>
|
|
1245
|
+
<div class="radio-separator">Jurisdiction (Compliance)</div>
|
|
1246
|
+
<label class="radio-item">
|
|
1247
|
+
<input type="radio" name="db-pii-location" value="eu">
|
|
1248
|
+
<span>EU Jurisdiction (GDPR compliance)</span>
|
|
1249
|
+
</label>
|
|
1250
|
+
</div>
|
|
1251
|
+
</div>
|
|
1252
|
+
</div>
|
|
1253
|
+
</div>
|
|
1254
|
+
|
|
1255
|
+
<div class="button-group">
|
|
1256
|
+
<button class="btn-secondary" id="btn-back-database">Back</button>
|
|
1257
|
+
<button class="btn-primary" id="btn-continue-database">Continue</button>
|
|
1258
|
+
</div>
|
|
1259
|
+
</div>
|
|
1260
|
+
|
|
1261
|
+
<!-- Step 4: Email Provider Configuration -->
|
|
1262
|
+
<div id="section-email" class="card hidden">
|
|
1263
|
+
<h2 class="card-title">📧 Email Provider</h2>
|
|
1264
|
+
|
|
1265
|
+
<p style="margin-bottom: 1rem; color: var(--text-muted);">
|
|
1266
|
+
Configure email sending for magic links and verification codes.
|
|
1267
|
+
You can configure this later if you prefer.
|
|
1268
|
+
</p>
|
|
1269
|
+
|
|
1270
|
+
<div class="radio-group" style="margin-bottom: 1.5rem;">
|
|
1271
|
+
<label class="radio-item" style="padding: 0.75rem; border: 1px solid var(--border); border-radius: 8px;">
|
|
1272
|
+
<input type="radio" name="email-setup-choice" value="later" checked>
|
|
1273
|
+
<span style="display: flex; flex-direction: column; gap: 0.25rem;">
|
|
1274
|
+
<strong>Configure later</strong>
|
|
1275
|
+
<small style="color: var(--text-muted);">Skip for now. Magic links will return URLs instead of sending emails.</small>
|
|
1276
|
+
</span>
|
|
1277
|
+
</label>
|
|
1278
|
+
<label class="radio-item" style="padding: 0.75rem; border: 1px solid var(--border); border-radius: 8px; margin-top: 0.5rem;">
|
|
1279
|
+
<input type="radio" name="email-setup-choice" value="configure">
|
|
1280
|
+
<span style="display: flex; flex-direction: column; gap: 0.25rem;">
|
|
1281
|
+
<strong>Configure Resend</strong>
|
|
1282
|
+
<small style="color: var(--text-muted);">Set up email sending with Resend (recommended for production).</small>
|
|
1283
|
+
</span>
|
|
1284
|
+
</label>
|
|
1285
|
+
</div>
|
|
1286
|
+
|
|
1287
|
+
<!-- Resend Configuration Form (hidden by default) -->
|
|
1288
|
+
<div id="resend-config-form" class="hidden" style="background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem;">
|
|
1289
|
+
<h3 style="margin: 0 0 1rem 0; font-size: 1rem;">🔑 Resend Configuration</h3>
|
|
1290
|
+
|
|
1291
|
+
<div class="alert alert-info" style="margin-bottom: 1rem;">
|
|
1292
|
+
<strong>📋 Before you begin:</strong>
|
|
1293
|
+
<ol style="margin: 0.5rem 0 0 1rem; padding: 0;">
|
|
1294
|
+
<li>Create a Resend account at <a href="https://resend.com" target="_blank" style="color: var(--primary);">resend.com</a></li>
|
|
1295
|
+
<li>Add and verify your domain at <a href="https://resend.com/domains" target="_blank" style="color: var(--primary);">Domains Dashboard</a></li>
|
|
1296
|
+
<li>Create an API key at <a href="https://resend.com/api-keys" target="_blank" style="color: var(--primary);">API Keys</a></li>
|
|
1297
|
+
</ol>
|
|
1298
|
+
</div>
|
|
1299
|
+
|
|
1300
|
+
<div class="form-group">
|
|
1301
|
+
<label for="resend-api-key">Resend API Key</label>
|
|
1302
|
+
<input type="password" id="resend-api-key" placeholder="re_xxxxxxxxxx" autocomplete="off">
|
|
1303
|
+
<small style="color: var(--text-muted);">Your API key starts with "re_"</small>
|
|
1304
|
+
</div>
|
|
1305
|
+
|
|
1306
|
+
<div class="form-group">
|
|
1307
|
+
<label for="email-from-address">From Email Address</label>
|
|
1308
|
+
<input type="email" id="email-from-address" placeholder="noreply@yourdomain.com" autocomplete="off">
|
|
1309
|
+
<small style="color: var(--text-muted);">Must be from a verified domain in your Resend account</small>
|
|
1310
|
+
</div>
|
|
1311
|
+
|
|
1312
|
+
<div class="form-group">
|
|
1313
|
+
<label for="email-from-name">From Display Name (optional)</label>
|
|
1314
|
+
<input type="text" id="email-from-name" placeholder="Authrim" autocomplete="off">
|
|
1315
|
+
<small style="color: var(--text-muted);">Displayed as the sender name in email clients</small>
|
|
1316
|
+
</div>
|
|
1317
|
+
|
|
1318
|
+
<div class="alert alert-warning" style="margin-top: 1rem;">
|
|
1319
|
+
<strong>⚠️ Domain Verification Required</strong>
|
|
1320
|
+
<p style="margin: 0.25rem 0 0 0; font-size: 0.875rem;">
|
|
1321
|
+
Before your domain is verified, emails can only be sent from <code>onboarding@resend.dev</code> (for testing).
|
|
1322
|
+
<a href="https://resend.com/docs/dashboard/domains/introduction" target="_blank" style="color: var(--primary);">Learn more about domain verification →</a>
|
|
1323
|
+
</p>
|
|
1324
|
+
</div>
|
|
1325
|
+
</div>
|
|
1326
|
+
|
|
1327
|
+
<div class="button-group">
|
|
1328
|
+
<button class="btn-secondary" id="btn-back-email">Back</button>
|
|
1329
|
+
<button class="btn-primary" id="btn-continue-email">Continue</button>
|
|
1330
|
+
</div>
|
|
1331
|
+
</div>
|
|
1332
|
+
|
|
1333
|
+
<!-- Step 5: Provisioning -->
|
|
1030
1334
|
<div id="section-provision" class="card hidden">
|
|
1031
1335
|
<h2 class="card-title">
|
|
1032
1336
|
Resource Provisioning
|
|
@@ -1298,6 +1602,10 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1298
1602
|
2: document.getElementById('step-2'),
|
|
1299
1603
|
3: document.getElementById('step-3'),
|
|
1300
1604
|
4: document.getElementById('step-4'),
|
|
1605
|
+
5: document.getElementById('step-5'),
|
|
1606
|
+
6: document.getElementById('step-6'),
|
|
1607
|
+
7: document.getElementById('step-7'),
|
|
1608
|
+
8: document.getElementById('step-8'),
|
|
1301
1609
|
};
|
|
1302
1610
|
|
|
1303
1611
|
const sections = {
|
|
@@ -1306,6 +1614,8 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1306
1614
|
mode: document.getElementById('section-mode'),
|
|
1307
1615
|
loadConfig: document.getElementById('section-load-config'),
|
|
1308
1616
|
config: document.getElementById('section-config'),
|
|
1617
|
+
database: document.getElementById('section-database'),
|
|
1618
|
+
email: document.getElementById('section-email'),
|
|
1309
1619
|
provision: document.getElementById('section-provision'),
|
|
1310
1620
|
deploy: document.getElementById('section-deploy'),
|
|
1311
1621
|
complete: document.getElementById('section-complete'),
|
|
@@ -1337,7 +1647,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1337
1647
|
// Step navigation
|
|
1338
1648
|
function setStep(step) {
|
|
1339
1649
|
currentStep = step;
|
|
1340
|
-
for (let i = 1; i <=
|
|
1650
|
+
for (let i = 1; i <= 7; i++) {
|
|
1341
1651
|
const el = steps[i];
|
|
1342
1652
|
el.className = 'step ' + (i < step ? 'step-complete' : i === step ? 'step-active' : 'step-pending');
|
|
1343
1653
|
}
|
|
@@ -1568,28 +1878,80 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1568
1878
|
document.getElementById('btn-load-config').addEventListener('click', async () => {
|
|
1569
1879
|
if (!loadedConfig) return;
|
|
1570
1880
|
|
|
1571
|
-
//
|
|
1881
|
+
// Support both new format (v1.0.0) and old format (v0.1.x)
|
|
1882
|
+
const isNewFormat = loadedConfig.version === '1.0.0' || loadedConfig.environment?.prefix;
|
|
1883
|
+
|
|
1884
|
+
// Extract values (with fallback for old format)
|
|
1885
|
+
const env = isNewFormat
|
|
1886
|
+
? loadedConfig.environment?.prefix
|
|
1887
|
+
: loadedConfig.env || 'prod';
|
|
1888
|
+
|
|
1889
|
+
const apiDomain = isNewFormat
|
|
1890
|
+
? loadedConfig.urls?.api?.custom
|
|
1891
|
+
: loadedConfig.apiDomain;
|
|
1892
|
+
|
|
1893
|
+
const loginUiDomain = isNewFormat
|
|
1894
|
+
? loadedConfig.urls?.loginUi?.custom
|
|
1895
|
+
: loadedConfig.loginUiDomain;
|
|
1896
|
+
|
|
1897
|
+
const adminUiDomain = isNewFormat
|
|
1898
|
+
? loadedConfig.urls?.adminUi?.custom
|
|
1899
|
+
: loadedConfig.adminUiDomain;
|
|
1900
|
+
|
|
1901
|
+
const tenant = loadedConfig.tenant || {
|
|
1902
|
+
name: 'default',
|
|
1903
|
+
displayName: 'Default Tenant',
|
|
1904
|
+
multiTenant: false,
|
|
1905
|
+
};
|
|
1906
|
+
|
|
1907
|
+
const components = loadedConfig.components || {
|
|
1908
|
+
api: true,
|
|
1909
|
+
loginUi: true,
|
|
1910
|
+
adminUi: true,
|
|
1911
|
+
saml: false,
|
|
1912
|
+
async: false,
|
|
1913
|
+
vc: false,
|
|
1914
|
+
bridge: true,
|
|
1915
|
+
policy: true,
|
|
1916
|
+
};
|
|
1917
|
+
|
|
1918
|
+
// Build internal config
|
|
1572
1919
|
config = {
|
|
1573
|
-
env
|
|
1574
|
-
apiDomain:
|
|
1575
|
-
loginUiDomain:
|
|
1576
|
-
adminUiDomain:
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
loginUi: true,
|
|
1580
|
-
adminUi: true,
|
|
1581
|
-
saml: false,
|
|
1582
|
-
async: false,
|
|
1583
|
-
vc: false,
|
|
1584
|
-
},
|
|
1920
|
+
env,
|
|
1921
|
+
apiDomain: apiDomain || null,
|
|
1922
|
+
loginUiDomain: loginUiDomain || null,
|
|
1923
|
+
adminUiDomain: adminUiDomain || null,
|
|
1924
|
+
tenant,
|
|
1925
|
+
components,
|
|
1585
1926
|
};
|
|
1586
1927
|
|
|
1587
1928
|
// Set form values
|
|
1588
1929
|
document.getElementById('env').value = config.env;
|
|
1589
|
-
document.getElementById('
|
|
1930
|
+
document.getElementById('base-domain').value = config.tenant?.baseDomain || config.apiDomain || '';
|
|
1590
1931
|
document.getElementById('login-domain').value = config.loginUiDomain || '';
|
|
1591
1932
|
document.getElementById('admin-domain').value = config.adminUiDomain || '';
|
|
1592
|
-
|
|
1933
|
+
document.getElementById('tenant-name').value = config.tenant?.name || 'default';
|
|
1934
|
+
document.getElementById('tenant-display').value = config.tenant?.displayName || 'Default Tenant';
|
|
1935
|
+
document.getElementById('naked-domain').checked = config.tenant?.nakedDomain || false;
|
|
1936
|
+
|
|
1937
|
+
// Set component checkboxes
|
|
1938
|
+
if (document.getElementById('comp-login-ui')) {
|
|
1939
|
+
document.getElementById('comp-login-ui').checked = components.loginUi !== false;
|
|
1940
|
+
}
|
|
1941
|
+
if (document.getElementById('comp-admin-ui')) {
|
|
1942
|
+
document.getElementById('comp-admin-ui').checked = components.adminUi !== false;
|
|
1943
|
+
}
|
|
1944
|
+
if (document.getElementById('comp-saml')) {
|
|
1945
|
+
document.getElementById('comp-saml').checked = components.saml === true;
|
|
1946
|
+
}
|
|
1947
|
+
if (document.getElementById('comp-async')) {
|
|
1948
|
+
document.getElementById('comp-async').checked = components.async === true;
|
|
1949
|
+
}
|
|
1950
|
+
if (document.getElementById('comp-vc')) {
|
|
1951
|
+
document.getElementById('comp-vc').checked = components.vc === true;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
// Trigger env input to update preview/default labels
|
|
1593
1955
|
document.getElementById('env').dispatchEvent(new Event('input'));
|
|
1594
1956
|
|
|
1595
1957
|
// Skip to provisioning if resources already exist
|
|
@@ -1864,15 +2226,145 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1864
2226
|
updateResourcePreview(env);
|
|
1865
2227
|
updateProvisionButtons();
|
|
1866
2228
|
|
|
1867
|
-
|
|
1868
|
-
|
|
2229
|
+
// Go to database configuration step
|
|
2230
|
+
setStep(4);
|
|
2231
|
+
showSection('database');
|
|
1869
2232
|
});
|
|
1870
2233
|
|
|
1871
2234
|
document.getElementById('btn-back-config').addEventListener('click', () => {
|
|
1872
|
-
|
|
2235
|
+
// Go back to email configuration (previous step in the flow)
|
|
2236
|
+
setStep(5);
|
|
2237
|
+
showSection('email');
|
|
2238
|
+
});
|
|
2239
|
+
|
|
2240
|
+
// Database configuration handlers
|
|
2241
|
+
document.getElementById('btn-back-database').addEventListener('click', () => {
|
|
2242
|
+
setStep(3);
|
|
1873
2243
|
showSection('config');
|
|
1874
2244
|
});
|
|
1875
2245
|
|
|
2246
|
+
document.getElementById('btn-continue-database').addEventListener('click', () => {
|
|
2247
|
+
// Get selected values
|
|
2248
|
+
const coreLocation = document.querySelector('input[name="db-core-location"]:checked').value;
|
|
2249
|
+
const piiLocation = document.querySelector('input[name="db-pii-location"]:checked').value;
|
|
2250
|
+
|
|
2251
|
+
// Parse location vs jurisdiction
|
|
2252
|
+
function parseDbLocation(value) {
|
|
2253
|
+
if (value === 'eu') {
|
|
2254
|
+
return { location: 'auto', jurisdiction: 'eu' };
|
|
2255
|
+
}
|
|
2256
|
+
return { location: value, jurisdiction: 'none' };
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// Add database config to config object
|
|
2260
|
+
config.database = {
|
|
2261
|
+
core: parseDbLocation(coreLocation),
|
|
2262
|
+
pii: parseDbLocation(piiLocation),
|
|
2263
|
+
};
|
|
2264
|
+
|
|
2265
|
+
// Proceed to email configuration
|
|
2266
|
+
setStep(5);
|
|
2267
|
+
showSection('email');
|
|
2268
|
+
});
|
|
2269
|
+
|
|
2270
|
+
// Email configuration handlers
|
|
2271
|
+
// Toggle resend config form visibility
|
|
2272
|
+
document.querySelectorAll('input[name="email-setup-choice"]').forEach(radio => {
|
|
2273
|
+
radio.addEventListener('change', () => {
|
|
2274
|
+
const resendForm = document.getElementById('resend-config-form');
|
|
2275
|
+
const choice = document.querySelector('input[name="email-setup-choice"]:checked').value;
|
|
2276
|
+
if (choice === 'configure') {
|
|
2277
|
+
resendForm.classList.remove('hidden');
|
|
2278
|
+
} else {
|
|
2279
|
+
resendForm.classList.add('hidden');
|
|
2280
|
+
}
|
|
2281
|
+
});
|
|
2282
|
+
});
|
|
2283
|
+
|
|
2284
|
+
document.getElementById('btn-back-email').addEventListener('click', () => {
|
|
2285
|
+
setStep(4);
|
|
2286
|
+
showSection('database');
|
|
2287
|
+
});
|
|
2288
|
+
|
|
2289
|
+
document.getElementById('btn-continue-email').addEventListener('click', async () => {
|
|
2290
|
+
const choice = document.querySelector('input[name="email-setup-choice"]:checked').value;
|
|
2291
|
+
const btn = document.getElementById('btn-continue-email');
|
|
2292
|
+
|
|
2293
|
+
if (choice === 'configure') {
|
|
2294
|
+
// Validate and store email configuration
|
|
2295
|
+
const apiKey = document.getElementById('resend-api-key').value.trim();
|
|
2296
|
+
const fromAddress = document.getElementById('email-from-address').value.trim();
|
|
2297
|
+
const fromName = document.getElementById('email-from-name').value.trim();
|
|
2298
|
+
|
|
2299
|
+
// Validate API key format
|
|
2300
|
+
if (!apiKey) {
|
|
2301
|
+
alert('Please enter your Resend API key');
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
if (!apiKey.startsWith('re_')) {
|
|
2305
|
+
if (!confirm('API key does not start with "re_". This may not be a valid Resend API key. Continue anyway?')) {
|
|
2306
|
+
return;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// Validate email address
|
|
2311
|
+
if (!fromAddress) {
|
|
2312
|
+
alert('Please enter a From email address');
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
if (!fromAddress.includes('@')) {
|
|
2316
|
+
alert('Please enter a valid email address');
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
// Save email configuration to server
|
|
2321
|
+
btn.disabled = true;
|
|
2322
|
+
btn.textContent = 'Saving...';
|
|
2323
|
+
|
|
2324
|
+
try {
|
|
2325
|
+
const result = await api('/email/configure', {
|
|
2326
|
+
method: 'POST',
|
|
2327
|
+
body: {
|
|
2328
|
+
env: config.env,
|
|
2329
|
+
provider: 'resend',
|
|
2330
|
+
apiKey: apiKey,
|
|
2331
|
+
fromAddress: fromAddress,
|
|
2332
|
+
fromName: fromName || undefined,
|
|
2333
|
+
},
|
|
2334
|
+
});
|
|
2335
|
+
|
|
2336
|
+
if (!result.success) {
|
|
2337
|
+
throw new Error(result.error || 'Failed to save email configuration');
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
// Store email configuration (without apiKey for config file)
|
|
2341
|
+
config.email = {
|
|
2342
|
+
provider: 'resend',
|
|
2343
|
+
fromAddress: fromAddress,
|
|
2344
|
+
fromName: fromName || undefined,
|
|
2345
|
+
configured: true,
|
|
2346
|
+
};
|
|
2347
|
+
} catch (error) {
|
|
2348
|
+
alert('Failed to save email configuration: ' + error.message);
|
|
2349
|
+
btn.disabled = false;
|
|
2350
|
+
btn.textContent = 'Continue';
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
btn.disabled = false;
|
|
2355
|
+
btn.textContent = 'Continue';
|
|
2356
|
+
} else {
|
|
2357
|
+
// Configure later - no email provider
|
|
2358
|
+
config.email = {
|
|
2359
|
+
provider: 'none',
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
// Proceed to provision
|
|
2364
|
+
setStep(6);
|
|
2365
|
+
showSection('provision');
|
|
2366
|
+
});
|
|
2367
|
+
|
|
1876
2368
|
// Provision
|
|
1877
2369
|
document.getElementById('btn-provision').addEventListener('click', async () => {
|
|
1878
2370
|
const btn = document.getElementById('btn-provision');
|
|
@@ -1944,7 +2436,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1944
2436
|
|
|
1945
2437
|
const result = await api('/provision', {
|
|
1946
2438
|
method: 'POST',
|
|
1947
|
-
body: { env: config.env },
|
|
2439
|
+
body: { env: config.env, databaseConfig: config.database },
|
|
1948
2440
|
});
|
|
1949
2441
|
|
|
1950
2442
|
// Stop polling
|
|
@@ -1990,12 +2482,12 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
1990
2482
|
|
|
1991
2483
|
// Continue to Deploy button
|
|
1992
2484
|
document.getElementById('btn-goto-deploy').addEventListener('click', () => {
|
|
1993
|
-
setStep(
|
|
2485
|
+
setStep(7);
|
|
1994
2486
|
showSection('deploy');
|
|
1995
2487
|
});
|
|
1996
2488
|
|
|
1997
2489
|
document.getElementById('btn-back-provision').addEventListener('click', () => {
|
|
1998
|
-
setStep(
|
|
2490
|
+
setStep(6);
|
|
1999
2491
|
// Update buttons based on provisioning status
|
|
2000
2492
|
updateProvisionButtons();
|
|
2001
2493
|
// Show resource preview if not completed
|
|
@@ -2077,7 +2569,12 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
2077
2569
|
// Workers.dev - no tenant prefix (wildcard subdomains not supported)
|
|
2078
2570
|
apiUrl = 'https://' + workersDomain;
|
|
2079
2571
|
}
|
|
2572
|
+
// Login UI URL for setup page (setup page is in Login UI, not API)
|
|
2573
|
+
const pagesDomain = config.env + '-ar-ui.pages.dev';
|
|
2574
|
+
const loginUiUrl = config.loginUiDomain ? 'https://' + config.loginUiDomain : 'https://' + pagesDomain;
|
|
2575
|
+
|
|
2080
2576
|
output.textContent += ' API URL: ' + apiUrl + '\\n';
|
|
2577
|
+
output.textContent += ' Login UI URL: ' + loginUiUrl + '\\n';
|
|
2081
2578
|
output.textContent += ' Keys Dir: .keys/' + config.env + '\\n';
|
|
2082
2579
|
scrollToBottom(log);
|
|
2083
2580
|
|
|
@@ -2087,7 +2584,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
2087
2584
|
method: 'POST',
|
|
2088
2585
|
body: {
|
|
2089
2586
|
env: config.env,
|
|
2090
|
-
baseUrl:
|
|
2587
|
+
baseUrl: loginUiUrl, // Setup page is in Login UI
|
|
2091
2588
|
keysDir: '.keys',
|
|
2092
2589
|
},
|
|
2093
2590
|
});
|
|
@@ -2197,6 +2694,7 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
2197
2694
|
urlsEl.appendChild(debugDiv);
|
|
2198
2695
|
}
|
|
2199
2696
|
|
|
2697
|
+
setStep(8);
|
|
2200
2698
|
showSection('complete');
|
|
2201
2699
|
}
|
|
2202
2700
|
|
|
@@ -2279,19 +2777,97 @@ export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
|
2279
2777
|
}
|
|
2280
2778
|
}
|
|
2281
2779
|
|
|
2282
|
-
// Save configuration to file
|
|
2780
|
+
// Save configuration to file (AuthrimConfigSchema format)
|
|
2283
2781
|
function saveConfigToFile() {
|
|
2284
|
-
if (!config) {
|
|
2782
|
+
if (!config || !config.env) {
|
|
2285
2783
|
alert('No configuration to save');
|
|
2286
2784
|
return;
|
|
2287
2785
|
}
|
|
2288
2786
|
|
|
2787
|
+
const now = new Date().toISOString();
|
|
2788
|
+
const env = config.env;
|
|
2789
|
+
|
|
2790
|
+
// Calculate auto-generated URLs
|
|
2791
|
+
const workersDomain = env + '-ar-router.workers.dev';
|
|
2792
|
+
const pagesDomain = env + '-ar-ui.pages.dev';
|
|
2793
|
+
|
|
2794
|
+
// Build issuer URL based on tenant settings
|
|
2795
|
+
let issuerAutoUrl = 'https://' + workersDomain;
|
|
2796
|
+
if (config.tenant && config.tenant.baseDomain) {
|
|
2797
|
+
if (config.tenant.nakedDomain) {
|
|
2798
|
+
issuerAutoUrl = 'https://' + config.tenant.baseDomain;
|
|
2799
|
+
} else {
|
|
2800
|
+
const tenantName = config.tenant.name || 'default';
|
|
2801
|
+
issuerAutoUrl = 'https://' + tenantName + '.' + config.tenant.baseDomain;
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
// Build config in AuthrimConfigSchema format
|
|
2289
2806
|
const configToSave = {
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2807
|
+
version: '1.0.0',
|
|
2808
|
+
createdAt: now,
|
|
2809
|
+
updatedAt: now,
|
|
2810
|
+
environment: {
|
|
2811
|
+
prefix: env,
|
|
2812
|
+
},
|
|
2813
|
+
urls: {
|
|
2814
|
+
api: {
|
|
2815
|
+
custom: config.apiDomain || null,
|
|
2816
|
+
auto: config.apiDomain ? issuerAutoUrl : 'https://' + workersDomain,
|
|
2817
|
+
},
|
|
2818
|
+
loginUi: {
|
|
2819
|
+
custom: config.loginUiDomain || null,
|
|
2820
|
+
auto: 'https://' + pagesDomain,
|
|
2821
|
+
},
|
|
2822
|
+
adminUi: {
|
|
2823
|
+
custom: config.adminUiDomain || null,
|
|
2824
|
+
auto: 'https://' + pagesDomain + '/admin',
|
|
2825
|
+
},
|
|
2826
|
+
},
|
|
2827
|
+
tenant: {
|
|
2828
|
+
name: config.tenant?.name || 'default',
|
|
2829
|
+
displayName: config.tenant?.displayName || 'Default Tenant',
|
|
2830
|
+
multiTenant: config.tenant?.multiTenant || false,
|
|
2831
|
+
baseDomain: config.tenant?.baseDomain || undefined,
|
|
2832
|
+
},
|
|
2833
|
+
components: config.components || {
|
|
2834
|
+
api: true,
|
|
2835
|
+
loginUi: true,
|
|
2836
|
+
adminUi: true,
|
|
2837
|
+
saml: false,
|
|
2838
|
+
async: false,
|
|
2839
|
+
vc: false,
|
|
2840
|
+
bridge: true,
|
|
2841
|
+
policy: true,
|
|
2842
|
+
},
|
|
2843
|
+
keys: {
|
|
2844
|
+
secretsPath: './.keys/',
|
|
2845
|
+
},
|
|
2846
|
+
database: config.database || {
|
|
2847
|
+
core: { location: 'auto', jurisdiction: 'none' },
|
|
2848
|
+
pii: { location: 'auto', jurisdiction: 'none' },
|
|
2849
|
+
},
|
|
2850
|
+
features: {
|
|
2851
|
+
email: {
|
|
2852
|
+
provider: config.email?.provider || 'none',
|
|
2853
|
+
fromAddress: config.email?.fromAddress || undefined,
|
|
2854
|
+
fromName: config.email?.fromName || undefined,
|
|
2855
|
+
configured: config.email?.provider === 'resend' && config.email?.apiKey ? true : false,
|
|
2856
|
+
},
|
|
2857
|
+
},
|
|
2293
2858
|
};
|
|
2294
2859
|
|
|
2860
|
+
// Remove undefined values for cleaner output
|
|
2861
|
+
if (!configToSave.tenant.baseDomain) {
|
|
2862
|
+
delete configToSave.tenant.baseDomain;
|
|
2863
|
+
}
|
|
2864
|
+
if (!configToSave.features.email.fromAddress) {
|
|
2865
|
+
delete configToSave.features.email.fromAddress;
|
|
2866
|
+
}
|
|
2867
|
+
if (!configToSave.features.email.fromName) {
|
|
2868
|
+
delete configToSave.features.email.fromName;
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2295
2871
|
const blob = new Blob([JSON.stringify(configToSave, null, 2)], { type: 'application/json' });
|
|
2296
2872
|
const url = URL.createObjectURL(blob);
|
|
2297
2873
|
const a = document.createElement('a');
|