@authrim/setup 0.1.5 → 0.1.7
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 +163 -24
- package/dist/cli/commands/init.js +1 -1
- package/dist/core/cloudflare.d.ts +103 -0
- package/dist/core/cloudflare.d.ts.map +1 -1
- package/dist/core/cloudflare.js +465 -14
- package/dist/core/cloudflare.js.map +1 -1
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +82 -1
- package/dist/web/api.js.map +1 -1
- package/dist/web/server.d.ts +2 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +2 -2
- package/dist/web/server.js.map +1 -1
- package/dist/web/ui.d.ts +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +1049 -19
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/web/ui.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* A simple, self-contained UI for the setup wizard.
|
|
5
5
|
* Follows the setup flow defined in the design document.
|
|
6
6
|
*/
|
|
7
|
-
export function getHtmlTemplate(sessionToken) {
|
|
7
|
+
export function getHtmlTemplate(sessionToken, manageOnly) {
|
|
8
8
|
// Escape token for safe embedding in JavaScript
|
|
9
9
|
const safeToken = sessionToken ? sessionToken.replace(/['"\\]/g, '') : '';
|
|
10
|
+
const manageOnlyFlag = manageOnly ? 'true' : 'false';
|
|
10
11
|
return `<!DOCTYPE html>
|
|
11
12
|
<html lang="en">
|
|
12
13
|
<head>
|
|
@@ -365,6 +366,256 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
365
366
|
}
|
|
366
367
|
|
|
367
368
|
.hidden { display: none; }
|
|
369
|
+
|
|
370
|
+
/* Resource preview styles */
|
|
371
|
+
.resource-preview {
|
|
372
|
+
background: var(--bg);
|
|
373
|
+
border: 1px solid var(--border);
|
|
374
|
+
border-radius: 8px;
|
|
375
|
+
padding: 1rem;
|
|
376
|
+
margin-bottom: 1rem;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.resource-list {
|
|
380
|
+
display: grid;
|
|
381
|
+
gap: 1rem;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.resource-category {
|
|
385
|
+
font-size: 0.875rem;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.resource-category strong {
|
|
389
|
+
display: block;
|
|
390
|
+
margin-bottom: 0.5rem;
|
|
391
|
+
color: var(--text);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.resource-category ul {
|
|
395
|
+
margin: 0;
|
|
396
|
+
padding-left: 1.5rem;
|
|
397
|
+
color: var(--text-muted);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.resource-category li {
|
|
401
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
402
|
+
font-size: 0.8rem;
|
|
403
|
+
margin-bottom: 0.25rem;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* Progress spinner */
|
|
407
|
+
.spinner {
|
|
408
|
+
display: inline-block;
|
|
409
|
+
width: 16px;
|
|
410
|
+
height: 16px;
|
|
411
|
+
border: 2px solid var(--border);
|
|
412
|
+
border-top-color: var(--primary);
|
|
413
|
+
border-radius: 50%;
|
|
414
|
+
animation: spin 1s linear infinite;
|
|
415
|
+
margin-right: 0.5rem;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
@keyframes spin {
|
|
419
|
+
to { transform: rotate(360deg); }
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.progress-item {
|
|
423
|
+
display: flex;
|
|
424
|
+
align-items: center;
|
|
425
|
+
margin-bottom: 0.5rem;
|
|
426
|
+
color: #e2e8f0;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.progress-item.complete {
|
|
430
|
+
color: var(--success);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.progress-item.error {
|
|
434
|
+
color: var(--error);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* Environment cards */
|
|
438
|
+
.env-cards {
|
|
439
|
+
display: grid;
|
|
440
|
+
gap: 1rem;
|
|
441
|
+
margin-bottom: 1rem;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.env-card {
|
|
445
|
+
border: 1px solid var(--border);
|
|
446
|
+
border-radius: 8px;
|
|
447
|
+
padding: 1rem;
|
|
448
|
+
display: flex;
|
|
449
|
+
justify-content: space-between;
|
|
450
|
+
align-items: center;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.env-card:hover {
|
|
454
|
+
border-color: var(--primary);
|
|
455
|
+
background: #f8fafc;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.env-card-info {
|
|
459
|
+
flex: 1;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.env-card-name {
|
|
463
|
+
font-size: 1.1rem;
|
|
464
|
+
font-weight: 600;
|
|
465
|
+
margin-bottom: 0.5rem;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.env-card-stats {
|
|
469
|
+
display: flex;
|
|
470
|
+
gap: 1rem;
|
|
471
|
+
font-size: 0.8rem;
|
|
472
|
+
color: var(--text-muted);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.env-card-stat {
|
|
476
|
+
display: flex;
|
|
477
|
+
align-items: center;
|
|
478
|
+
gap: 0.25rem;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.env-card-actions {
|
|
482
|
+
display: flex;
|
|
483
|
+
gap: 0.5rem;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.btn-danger {
|
|
487
|
+
background: var(--error);
|
|
488
|
+
color: white;
|
|
489
|
+
padding: 0.5rem 1rem;
|
|
490
|
+
font-size: 0.875rem;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.btn-danger:hover {
|
|
494
|
+
background: #dc2626;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.btn-info {
|
|
498
|
+
background: var(--primary);
|
|
499
|
+
color: white;
|
|
500
|
+
padding: 0.5rem 1rem;
|
|
501
|
+
font-size: 0.875rem;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.btn-info:hover {
|
|
505
|
+
background: var(--primary-dark);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* Resource list in details view */
|
|
509
|
+
.resource-section {
|
|
510
|
+
margin-bottom: 1.5rem;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.resource-section-title {
|
|
514
|
+
font-size: 1rem;
|
|
515
|
+
font-weight: 600;
|
|
516
|
+
margin-bottom: 0.75rem;
|
|
517
|
+
display: flex;
|
|
518
|
+
align-items: center;
|
|
519
|
+
gap: 0.5rem;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.resource-section-title .count {
|
|
523
|
+
font-size: 0.8rem;
|
|
524
|
+
color: var(--text-muted);
|
|
525
|
+
font-weight: normal;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.resource-list {
|
|
529
|
+
background: var(--bg);
|
|
530
|
+
border-radius: 8px;
|
|
531
|
+
padding: 0.75rem;
|
|
532
|
+
max-height: 200px;
|
|
533
|
+
overflow-y: auto;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.resource-item {
|
|
537
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
538
|
+
font-size: 0.8rem;
|
|
539
|
+
padding: 0.25rem 0.5rem;
|
|
540
|
+
margin-bottom: 0.25rem;
|
|
541
|
+
background: var(--card-bg);
|
|
542
|
+
border-radius: 4px;
|
|
543
|
+
border: 1px solid var(--border);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.resource-item:last-child {
|
|
547
|
+
margin-bottom: 0;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.resource-item-name {
|
|
551
|
+
font-weight: 500;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.resource-item-details {
|
|
555
|
+
font-size: 0.75rem;
|
|
556
|
+
color: var(--text-muted);
|
|
557
|
+
margin-top: 0.25rem;
|
|
558
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.resource-item-details span {
|
|
562
|
+
margin-right: 1rem;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.resource-item-loading {
|
|
566
|
+
color: var(--text-muted);
|
|
567
|
+
font-style: italic;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.resource-item-error {
|
|
571
|
+
color: var(--error);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.resource-item-not-deployed {
|
|
575
|
+
color: var(--warning);
|
|
576
|
+
font-style: italic;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.resource-empty {
|
|
580
|
+
color: var(--text-muted);
|
|
581
|
+
font-style: italic;
|
|
582
|
+
font-size: 0.875rem;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* Delete options */
|
|
586
|
+
.delete-options {
|
|
587
|
+
display: grid;
|
|
588
|
+
gap: 0.75rem;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.delete-option {
|
|
592
|
+
display: flex;
|
|
593
|
+
align-items: center;
|
|
594
|
+
gap: 0.75rem;
|
|
595
|
+
padding: 0.75rem;
|
|
596
|
+
border: 1px solid var(--border);
|
|
597
|
+
border-radius: 8px;
|
|
598
|
+
cursor: pointer;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.delete-option:hover {
|
|
602
|
+
background: var(--bg);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.delete-option input[type="checkbox"] {
|
|
606
|
+
width: 18px;
|
|
607
|
+
height: 18px;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.delete-option span {
|
|
611
|
+
display: flex;
|
|
612
|
+
flex-direction: column;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.delete-option small {
|
|
616
|
+
color: var(--text-muted);
|
|
617
|
+
font-size: 0.8rem;
|
|
618
|
+
}
|
|
368
619
|
</style>
|
|
369
620
|
</head>
|
|
370
621
|
<body>
|
|
@@ -395,12 +646,12 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
395
646
|
</div>
|
|
396
647
|
</div>
|
|
397
648
|
|
|
398
|
-
<!-- Step 1.5: Top Menu (New Setup / Load Config) -->
|
|
649
|
+
<!-- Step 1.5: Top Menu (New Setup / Load Config / Manage) -->
|
|
399
650
|
<div id="section-top-menu" class="card hidden">
|
|
400
651
|
<h2 class="card-title">Get Started</h2>
|
|
401
652
|
<p style="margin-bottom: 1.5rem; color: var(--text-muted);">Choose an option to continue:</p>
|
|
402
653
|
|
|
403
|
-
<div class="mode-cards">
|
|
654
|
+
<div class="mode-cards" style="grid-template-columns: repeat(3, 1fr);">
|
|
404
655
|
<div class="mode-card" id="menu-new-setup">
|
|
405
656
|
<div class="mode-icon">🆕</div>
|
|
406
657
|
<h3>New Setup</h3>
|
|
@@ -409,8 +660,14 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
409
660
|
|
|
410
661
|
<div class="mode-card" id="menu-load-config">
|
|
411
662
|
<div class="mode-icon">📂</div>
|
|
412
|
-
<h3>Load
|
|
413
|
-
<p>Resume or redeploy using
|
|
663
|
+
<h3>Load Config</h3>
|
|
664
|
+
<p>Resume or redeploy using existing config</p>
|
|
665
|
+
</div>
|
|
666
|
+
|
|
667
|
+
<div class="mode-card" id="menu-manage-env">
|
|
668
|
+
<div class="mode-icon">🗑️</div>
|
|
669
|
+
<h3>Manage Environments</h3>
|
|
670
|
+
<p>View, inspect, or delete existing environments</p>
|
|
414
671
|
</div>
|
|
415
672
|
</div>
|
|
416
673
|
</div>
|
|
@@ -545,19 +802,43 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
545
802
|
</h2>
|
|
546
803
|
|
|
547
804
|
<p style="margin-bottom: 1rem;">The following resources will be created:</p>
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
<
|
|
552
|
-
|
|
805
|
+
|
|
806
|
+
<!-- Resource names preview -->
|
|
807
|
+
<div id="resource-preview" class="resource-preview">
|
|
808
|
+
<h4 style="font-size: 0.9rem; margin-bottom: 0.75rem; color: var(--text-muted);">📋 Resource Names:</h4>
|
|
809
|
+
<div class="resource-list">
|
|
810
|
+
<div class="resource-category">
|
|
811
|
+
<strong>D1 Databases:</strong>
|
|
812
|
+
<ul id="preview-d1"></ul>
|
|
813
|
+
</div>
|
|
814
|
+
<div class="resource-category">
|
|
815
|
+
<strong>KV Namespaces:</strong>
|
|
816
|
+
<ul id="preview-kv"></ul>
|
|
817
|
+
</div>
|
|
818
|
+
<div class="resource-category">
|
|
819
|
+
<strong>Cryptographic Keys:</strong>
|
|
820
|
+
<ul id="preview-keys"></ul>
|
|
821
|
+
</div>
|
|
822
|
+
</div>
|
|
823
|
+
</div>
|
|
553
824
|
|
|
554
825
|
<div class="progress-log hidden" id="provision-log">
|
|
555
826
|
<pre id="provision-output"></pre>
|
|
556
827
|
</div>
|
|
557
828
|
|
|
829
|
+
<!-- Keys saved location (shown after completion) -->
|
|
830
|
+
<div id="keys-saved-info" class="alert alert-info hidden" style="margin-top: 1rem;">
|
|
831
|
+
<strong>🔑 Keys saved to:</strong>
|
|
832
|
+
<code style="display: block; margin-top: 0.5rem; padding: 0.5rem; background: #f1f5f9; border-radius: 4px;" id="keys-path"></code>
|
|
833
|
+
<p style="margin-top: 0.5rem; font-size: 0.85rem; color: var(--text-muted);">
|
|
834
|
+
⚠️ Keep this directory safe and add it to .gitignore
|
|
835
|
+
</p>
|
|
836
|
+
</div>
|
|
837
|
+
|
|
558
838
|
<div class="button-group">
|
|
559
839
|
<button class="btn-secondary" id="btn-back-config">Back</button>
|
|
560
840
|
<button class="btn-primary" id="btn-provision">Create Resources</button>
|
|
841
|
+
<button class="btn-primary hidden" id="btn-goto-deploy">Continue to Deploy →</button>
|
|
561
842
|
</div>
|
|
562
843
|
</div>
|
|
563
844
|
|
|
@@ -601,17 +882,178 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
601
882
|
</ol>
|
|
602
883
|
</div>
|
|
603
884
|
</div>
|
|
885
|
+
|
|
886
|
+
<!-- Environment Management: List -->
|
|
887
|
+
<div id="section-env-list" class="card hidden">
|
|
888
|
+
<h2 class="card-title">
|
|
889
|
+
Manage Environments
|
|
890
|
+
<span class="status-badge status-pending" id="env-list-status">Loading...</span>
|
|
891
|
+
</h2>
|
|
892
|
+
|
|
893
|
+
<p style="margin-bottom: 1rem; color: var(--text-muted);">
|
|
894
|
+
Detected Authrim environments in your Cloudflare account:
|
|
895
|
+
</p>
|
|
896
|
+
|
|
897
|
+
<div id="env-list-loading" class="progress-log">
|
|
898
|
+
<pre id="env-scan-output"></pre>
|
|
899
|
+
</div>
|
|
900
|
+
|
|
901
|
+
<div id="env-list-content" class="hidden">
|
|
902
|
+
<div id="env-cards" class="env-cards">
|
|
903
|
+
<!-- Environment cards will be inserted here -->
|
|
904
|
+
</div>
|
|
905
|
+
|
|
906
|
+
<div id="no-envs-message" class="alert alert-info hidden">
|
|
907
|
+
No Authrim environments detected in this Cloudflare account.
|
|
908
|
+
</div>
|
|
909
|
+
</div>
|
|
910
|
+
|
|
911
|
+
<div class="button-group">
|
|
912
|
+
<button class="btn-secondary" id="btn-back-env-list">Back</button>
|
|
913
|
+
<button class="btn-secondary" id="btn-refresh-env-list">🔄 Refresh</button>
|
|
914
|
+
</div>
|
|
915
|
+
</div>
|
|
916
|
+
|
|
917
|
+
<!-- Environment Management: Details -->
|
|
918
|
+
<div id="section-env-detail" class="card hidden">
|
|
919
|
+
<h2 class="card-title">
|
|
920
|
+
📋 Environment Details
|
|
921
|
+
<code id="detail-env-name" style="background: var(--bg); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 1rem;"></code>
|
|
922
|
+
</h2>
|
|
923
|
+
|
|
924
|
+
<div id="detail-resources">
|
|
925
|
+
<!-- Workers -->
|
|
926
|
+
<div class="resource-section">
|
|
927
|
+
<div class="resource-section-title">
|
|
928
|
+
🔧 Workers <span class="count" id="detail-workers-count">(0)</span>
|
|
929
|
+
</div>
|
|
930
|
+
<div class="resource-list" id="detail-workers-list"></div>
|
|
931
|
+
</div>
|
|
932
|
+
|
|
933
|
+
<!-- D1 Databases -->
|
|
934
|
+
<div class="resource-section">
|
|
935
|
+
<div class="resource-section-title">
|
|
936
|
+
📊 D1 Databases <span class="count" id="detail-d1-count">(0)</span>
|
|
937
|
+
</div>
|
|
938
|
+
<div class="resource-list" id="detail-d1-list"></div>
|
|
939
|
+
</div>
|
|
940
|
+
|
|
941
|
+
<!-- KV Namespaces -->
|
|
942
|
+
<div class="resource-section">
|
|
943
|
+
<div class="resource-section-title">
|
|
944
|
+
🗄️ KV Namespaces <span class="count" id="detail-kv-count">(0)</span>
|
|
945
|
+
</div>
|
|
946
|
+
<div class="resource-list" id="detail-kv-list"></div>
|
|
947
|
+
</div>
|
|
948
|
+
|
|
949
|
+
<!-- Queues -->
|
|
950
|
+
<div class="resource-section" id="detail-queues-section">
|
|
951
|
+
<div class="resource-section-title">
|
|
952
|
+
📨 Queues <span class="count" id="detail-queues-count">(0)</span>
|
|
953
|
+
</div>
|
|
954
|
+
<div class="resource-list" id="detail-queues-list"></div>
|
|
955
|
+
</div>
|
|
956
|
+
|
|
957
|
+
<!-- R2 Buckets -->
|
|
958
|
+
<div class="resource-section" id="detail-r2-section">
|
|
959
|
+
<div class="resource-section-title">
|
|
960
|
+
📁 R2 Buckets <span class="count" id="detail-r2-count">(0)</span>
|
|
961
|
+
</div>
|
|
962
|
+
<div class="resource-list" id="detail-r2-list"></div>
|
|
963
|
+
</div>
|
|
964
|
+
</div>
|
|
965
|
+
|
|
966
|
+
<div class="button-group">
|
|
967
|
+
<button class="btn-secondary" id="btn-back-env-detail">← Back to List</button>
|
|
968
|
+
<button class="btn-danger" id="btn-delete-from-detail">🗑️ Delete Environment...</button>
|
|
969
|
+
</div>
|
|
970
|
+
</div>
|
|
971
|
+
|
|
972
|
+
<!-- Environment Management: Delete Confirmation -->
|
|
973
|
+
<div id="section-env-delete" class="card hidden">
|
|
974
|
+
<h2 class="card-title" style="color: var(--error);">
|
|
975
|
+
⚠️ Delete Environment
|
|
976
|
+
</h2>
|
|
977
|
+
|
|
978
|
+
<div class="alert alert-warning">
|
|
979
|
+
<strong>Warning:</strong> This action is irreversible. All selected resources will be permanently deleted.
|
|
980
|
+
</div>
|
|
981
|
+
|
|
982
|
+
<div style="margin: 1.5rem 0;">
|
|
983
|
+
<h3 style="font-size: 1.1rem; margin-bottom: 1rem;">
|
|
984
|
+
Environment: <code id="delete-env-name" style="background: var(--bg); padding: 0.25rem 0.5rem; border-radius: 4px;"></code>
|
|
985
|
+
</h3>
|
|
986
|
+
|
|
987
|
+
<p style="margin-bottom: 1rem; color: var(--text-muted);">Select resources to delete:</p>
|
|
988
|
+
|
|
989
|
+
<div class="delete-options">
|
|
990
|
+
<label class="checkbox-item delete-option">
|
|
991
|
+
<input type="checkbox" id="delete-workers" checked>
|
|
992
|
+
<span>
|
|
993
|
+
<strong>Workers</strong>
|
|
994
|
+
<small id="delete-workers-count">(0 workers)</small>
|
|
995
|
+
</span>
|
|
996
|
+
</label>
|
|
997
|
+
|
|
998
|
+
<label class="checkbox-item delete-option">
|
|
999
|
+
<input type="checkbox" id="delete-d1" checked>
|
|
1000
|
+
<span>
|
|
1001
|
+
<strong>D1 Databases</strong>
|
|
1002
|
+
<small id="delete-d1-count">(0 databases)</small>
|
|
1003
|
+
</span>
|
|
1004
|
+
</label>
|
|
1005
|
+
|
|
1006
|
+
<label class="checkbox-item delete-option">
|
|
1007
|
+
<input type="checkbox" id="delete-kv" checked>
|
|
1008
|
+
<span>
|
|
1009
|
+
<strong>KV Namespaces</strong>
|
|
1010
|
+
<small id="delete-kv-count">(0 namespaces)</small>
|
|
1011
|
+
</span>
|
|
1012
|
+
</label>
|
|
1013
|
+
|
|
1014
|
+
<label class="checkbox-item delete-option">
|
|
1015
|
+
<input type="checkbox" id="delete-queues" checked>
|
|
1016
|
+
<span>
|
|
1017
|
+
<strong>Queues</strong>
|
|
1018
|
+
<small id="delete-queues-count">(0 queues)</small>
|
|
1019
|
+
</span>
|
|
1020
|
+
</label>
|
|
1021
|
+
|
|
1022
|
+
<label class="checkbox-item delete-option">
|
|
1023
|
+
<input type="checkbox" id="delete-r2" checked>
|
|
1024
|
+
<span>
|
|
1025
|
+
<strong>R2 Buckets</strong>
|
|
1026
|
+
<small id="delete-r2-count">(0 buckets)</small>
|
|
1027
|
+
</span>
|
|
1028
|
+
</label>
|
|
1029
|
+
</div>
|
|
1030
|
+
</div>
|
|
1031
|
+
|
|
1032
|
+
<div class="progress-log hidden" id="delete-log">
|
|
1033
|
+
<pre id="delete-output"></pre>
|
|
1034
|
+
</div>
|
|
1035
|
+
|
|
1036
|
+
<div id="delete-result" class="hidden"></div>
|
|
1037
|
+
|
|
1038
|
+
<div class="button-group">
|
|
1039
|
+
<button class="btn-secondary" id="btn-back-env-delete">Cancel</button>
|
|
1040
|
+
<button class="btn-primary" id="btn-confirm-delete" style="background: var(--error);">🗑️ Delete Selected</button>
|
|
1041
|
+
</div>
|
|
1042
|
+
</div>
|
|
604
1043
|
</div>
|
|
605
1044
|
|
|
606
1045
|
<script>
|
|
607
1046
|
// Session token for API authentication (embedded by server)
|
|
608
1047
|
const SESSION_TOKEN = '${safeToken}';
|
|
1048
|
+
const MANAGE_ONLY = ${manageOnlyFlag};
|
|
609
1049
|
|
|
610
1050
|
// State
|
|
611
1051
|
let currentStep = 1;
|
|
612
1052
|
let setupMode = 'quick'; // 'quick' or 'custom'
|
|
613
1053
|
let config = {};
|
|
614
1054
|
let loadedConfig = null;
|
|
1055
|
+
let provisioningCompleted = false;
|
|
1056
|
+
let provisionPollInterval = null;
|
|
615
1057
|
|
|
616
1058
|
// Elements
|
|
617
1059
|
const steps = {
|
|
@@ -630,8 +1072,16 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
630
1072
|
provision: document.getElementById('section-provision'),
|
|
631
1073
|
deploy: document.getElementById('section-deploy'),
|
|
632
1074
|
complete: document.getElementById('section-complete'),
|
|
1075
|
+
envList: document.getElementById('section-env-list'),
|
|
1076
|
+
envDetail: document.getElementById('section-env-detail'),
|
|
1077
|
+
envDelete: document.getElementById('section-env-delete'),
|
|
633
1078
|
};
|
|
634
1079
|
|
|
1080
|
+
// Environment management state
|
|
1081
|
+
let detectedEnvironments = [];
|
|
1082
|
+
let selectedEnvForDetail = null;
|
|
1083
|
+
let selectedEnvForDelete = null;
|
|
1084
|
+
|
|
635
1085
|
// API helpers (with session token authentication)
|
|
636
1086
|
async function api(endpoint, options = {}) {
|
|
637
1087
|
const response = await fetch('/api' + endpoint, {
|
|
@@ -948,6 +1398,10 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
948
1398
|
body: { env, domain },
|
|
949
1399
|
});
|
|
950
1400
|
|
|
1401
|
+
// Update resource preview with the selected env
|
|
1402
|
+
updateResourcePreview(env);
|
|
1403
|
+
updateProvisionButtons();
|
|
1404
|
+
|
|
951
1405
|
setStep(3);
|
|
952
1406
|
showSection('provision');
|
|
953
1407
|
});
|
|
@@ -960,52 +1414,119 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
960
1414
|
// Provision
|
|
961
1415
|
document.getElementById('btn-provision').addEventListener('click', async () => {
|
|
962
1416
|
const btn = document.getElementById('btn-provision');
|
|
1417
|
+
const btnGotoDeploy = document.getElementById('btn-goto-deploy');
|
|
963
1418
|
const status = document.getElementById('provision-status');
|
|
964
1419
|
const log = document.getElementById('provision-log');
|
|
965
1420
|
const output = document.getElementById('provision-output');
|
|
1421
|
+
const resourcePreview = document.getElementById('resource-preview');
|
|
1422
|
+
const keysSavedInfo = document.getElementById('keys-saved-info');
|
|
1423
|
+
const keysPath = document.getElementById('keys-path');
|
|
966
1424
|
|
|
967
1425
|
btn.disabled = true;
|
|
1426
|
+
btnGotoDeploy.classList.add('hidden');
|
|
968
1427
|
status.textContent = 'Running...';
|
|
969
1428
|
status.className = 'status-badge status-running';
|
|
970
1429
|
log.classList.remove('hidden');
|
|
1430
|
+
resourcePreview.classList.add('hidden');
|
|
1431
|
+
keysSavedInfo.classList.add('hidden');
|
|
971
1432
|
output.textContent = '';
|
|
972
1433
|
|
|
1434
|
+
// Start polling for progress
|
|
1435
|
+
let lastProgressLength = 0;
|
|
1436
|
+
provisionPollInterval = setInterval(async () => {
|
|
1437
|
+
try {
|
|
1438
|
+
const statusResult = await api('/deploy/status');
|
|
1439
|
+
if (statusResult.progress && statusResult.progress.length > lastProgressLength) {
|
|
1440
|
+
// Append new progress messages
|
|
1441
|
+
const newMessages = statusResult.progress.slice(lastProgressLength);
|
|
1442
|
+
newMessages.forEach(msg => {
|
|
1443
|
+
output.textContent += msg + '\\n';
|
|
1444
|
+
});
|
|
1445
|
+
lastProgressLength = statusResult.progress.length;
|
|
1446
|
+
// Auto-scroll to bottom
|
|
1447
|
+
log.scrollTop = log.scrollHeight;
|
|
1448
|
+
}
|
|
1449
|
+
} catch (e) {
|
|
1450
|
+
// Ignore polling errors
|
|
1451
|
+
}
|
|
1452
|
+
}, 500);
|
|
1453
|
+
|
|
973
1454
|
try {
|
|
974
1455
|
// Generate keys
|
|
975
|
-
output.textContent += 'Generating cryptographic keys...\\n';
|
|
976
|
-
await api('/keys/generate', {
|
|
1456
|
+
output.textContent += '🔐 Generating cryptographic keys...\\n';
|
|
1457
|
+
const keyResult = await api('/keys/generate', {
|
|
977
1458
|
method: 'POST',
|
|
978
1459
|
body: { keyId: config.env + '-key-' + Date.now() },
|
|
979
1460
|
});
|
|
980
|
-
output.textContent += '✓
|
|
1461
|
+
output.textContent += ' ✓ RSA key pair generated\\n';
|
|
1462
|
+
output.textContent += ' ✓ Encryption keys generated\\n';
|
|
1463
|
+
output.textContent += ' ✓ Admin secrets generated\\n';
|
|
1464
|
+
output.textContent += '\\n';
|
|
1465
|
+
|
|
1466
|
+
// Show keys saved location (relative to authrim source directory)
|
|
1467
|
+
keysPath.textContent = './.keys/';
|
|
981
1468
|
|
|
982
1469
|
// Provision resources
|
|
983
|
-
output.textContent += 'Provisioning Cloudflare resources...\\n';
|
|
1470
|
+
output.textContent += '☁️ Provisioning Cloudflare resources...\\n';
|
|
1471
|
+
|
|
984
1472
|
const result = await api('/provision', {
|
|
985
1473
|
method: 'POST',
|
|
986
1474
|
body: { env: config.env },
|
|
987
1475
|
});
|
|
988
1476
|
|
|
1477
|
+
// Stop polling
|
|
1478
|
+
if (provisionPollInterval) {
|
|
1479
|
+
clearInterval(provisionPollInterval);
|
|
1480
|
+
provisionPollInterval = null;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
989
1483
|
if (result.success) {
|
|
990
|
-
output.textContent += '\\n
|
|
1484
|
+
output.textContent += '\\n✅ Provisioning complete!\\n';
|
|
991
1485
|
status.textContent = 'Complete';
|
|
992
1486
|
status.className = 'status-badge status-success';
|
|
993
1487
|
|
|
994
|
-
|
|
995
|
-
|
|
1488
|
+
// Mark provisioning as completed
|
|
1489
|
+
provisioningCompleted = true;
|
|
1490
|
+
|
|
1491
|
+
// Show keys saved info
|
|
1492
|
+
keysSavedInfo.classList.remove('hidden');
|
|
1493
|
+
|
|
1494
|
+
// Update buttons
|
|
1495
|
+
btn.textContent = 'Re-provision (Delete & Create)';
|
|
1496
|
+
btn.disabled = false;
|
|
1497
|
+
btnGotoDeploy.classList.remove('hidden');
|
|
996
1498
|
} else {
|
|
997
1499
|
throw new Error(result.error);
|
|
998
1500
|
}
|
|
999
1501
|
} catch (error) {
|
|
1000
|
-
|
|
1502
|
+
// Stop polling
|
|
1503
|
+
if (provisionPollInterval) {
|
|
1504
|
+
clearInterval(provisionPollInterval);
|
|
1505
|
+
provisionPollInterval = null;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
output.textContent += '\\n❌ Error: ' + error.message + '\\n';
|
|
1001
1509
|
status.textContent = 'Error';
|
|
1002
1510
|
status.className = 'status-badge status-error';
|
|
1003
1511
|
btn.disabled = false;
|
|
1512
|
+
resourcePreview.classList.remove('hidden');
|
|
1004
1513
|
}
|
|
1005
1514
|
});
|
|
1006
1515
|
|
|
1516
|
+
// Continue to Deploy button
|
|
1517
|
+
document.getElementById('btn-goto-deploy').addEventListener('click', () => {
|
|
1518
|
+
setStep(4);
|
|
1519
|
+
showSection('deploy');
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1007
1522
|
document.getElementById('btn-back-provision').addEventListener('click', () => {
|
|
1008
1523
|
setStep(3);
|
|
1524
|
+
// Update buttons based on provisioning status
|
|
1525
|
+
updateProvisionButtons();
|
|
1526
|
+
// Show resource preview if not completed
|
|
1527
|
+
if (!provisioningCompleted) {
|
|
1528
|
+
document.getElementById('resource-preview').classList.remove('hidden');
|
|
1529
|
+
}
|
|
1009
1530
|
showSection('provision');
|
|
1010
1531
|
});
|
|
1011
1532
|
|
|
@@ -1096,8 +1617,517 @@ export function getHtmlTemplate(sessionToken) {
|
|
|
1096
1617
|
showSection('complete');
|
|
1097
1618
|
}
|
|
1098
1619
|
|
|
1620
|
+
// Resource naming functions
|
|
1621
|
+
function getResourceNames(env) {
|
|
1622
|
+
const envUpper = env.toUpperCase();
|
|
1623
|
+
return {
|
|
1624
|
+
d1: [
|
|
1625
|
+
env + '-authrim-core-db',
|
|
1626
|
+
env + '-authrim-pii-db'
|
|
1627
|
+
],
|
|
1628
|
+
kv: [
|
|
1629
|
+
envUpper + '-CLIENTS_CACHE',
|
|
1630
|
+
envUpper + '-INITIAL_ACCESS_TOKENS',
|
|
1631
|
+
envUpper + '-SETTINGS',
|
|
1632
|
+
envUpper + '-REBAC_CACHE',
|
|
1633
|
+
envUpper + '-USER_CACHE',
|
|
1634
|
+
envUpper + '-AUTHRIM_CONFIG',
|
|
1635
|
+
envUpper + '-STATE_STORE',
|
|
1636
|
+
envUpper + '-CONSENT_CACHE'
|
|
1637
|
+
],
|
|
1638
|
+
keys: [
|
|
1639
|
+
'.keys/private.pem (RSA Private Key)',
|
|
1640
|
+
'.keys/public.jwk.json (JWK Public Key)',
|
|
1641
|
+
'.keys/rp_token_encryption_key.txt',
|
|
1642
|
+
'.keys/admin_api_secret.txt',
|
|
1643
|
+
'.keys/key_manager_secret.txt',
|
|
1644
|
+
'.keys/setup_token.txt'
|
|
1645
|
+
]
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
function updateResourcePreview(env) {
|
|
1650
|
+
const resources = getResourceNames(env);
|
|
1651
|
+
|
|
1652
|
+
const d1List = document.getElementById('preview-d1');
|
|
1653
|
+
const kvList = document.getElementById('preview-kv');
|
|
1654
|
+
const keysList = document.getElementById('preview-keys');
|
|
1655
|
+
|
|
1656
|
+
d1List.innerHTML = '';
|
|
1657
|
+
kvList.innerHTML = '';
|
|
1658
|
+
keysList.innerHTML = '';
|
|
1659
|
+
|
|
1660
|
+
resources.d1.forEach(name => {
|
|
1661
|
+
const li = document.createElement('li');
|
|
1662
|
+
li.textContent = name;
|
|
1663
|
+
d1List.appendChild(li);
|
|
1664
|
+
});
|
|
1665
|
+
|
|
1666
|
+
resources.kv.forEach(name => {
|
|
1667
|
+
const li = document.createElement('li');
|
|
1668
|
+
li.textContent = name;
|
|
1669
|
+
kvList.appendChild(li);
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
resources.keys.forEach(name => {
|
|
1673
|
+
const li = document.createElement('li');
|
|
1674
|
+
li.textContent = name;
|
|
1675
|
+
keysList.appendChild(li);
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// Update provision button state based on completion status
|
|
1680
|
+
function updateProvisionButtons() {
|
|
1681
|
+
const btnProvision = document.getElementById('btn-provision');
|
|
1682
|
+
const btnGotoDeploy = document.getElementById('btn-goto-deploy');
|
|
1683
|
+
|
|
1684
|
+
if (provisioningCompleted) {
|
|
1685
|
+
btnProvision.textContent = 'Re-provision (Delete & Create)';
|
|
1686
|
+
btnProvision.disabled = false;
|
|
1687
|
+
btnGotoDeploy.classList.remove('hidden');
|
|
1688
|
+
} else {
|
|
1689
|
+
btnProvision.textContent = 'Create Resources';
|
|
1690
|
+
btnProvision.disabled = false;
|
|
1691
|
+
btnGotoDeploy.classList.add('hidden');
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// =============================================================================
|
|
1696
|
+
// Environment Management
|
|
1697
|
+
// =============================================================================
|
|
1698
|
+
|
|
1699
|
+
// Menu handler for environment management
|
|
1700
|
+
document.getElementById('menu-manage-env').addEventListener('click', () => {
|
|
1701
|
+
loadEnvironments();
|
|
1702
|
+
showSection('envList');
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
// Load environments
|
|
1706
|
+
async function loadEnvironments() {
|
|
1707
|
+
const status = document.getElementById('env-list-status');
|
|
1708
|
+
const loading = document.getElementById('env-list-loading');
|
|
1709
|
+
const content = document.getElementById('env-list-content');
|
|
1710
|
+
const output = document.getElementById('env-scan-output');
|
|
1711
|
+
const noEnvsMessage = document.getElementById('no-envs-message');
|
|
1712
|
+
|
|
1713
|
+
status.textContent = 'Scanning...';
|
|
1714
|
+
status.className = 'status-badge status-running';
|
|
1715
|
+
loading.classList.remove('hidden');
|
|
1716
|
+
content.classList.add('hidden');
|
|
1717
|
+
output.textContent = '';
|
|
1718
|
+
|
|
1719
|
+
// Poll for progress
|
|
1720
|
+
let lastProgressLength = 0;
|
|
1721
|
+
const pollInterval = setInterval(async () => {
|
|
1722
|
+
try {
|
|
1723
|
+
const statusResult = await api('/deploy/status');
|
|
1724
|
+
if (statusResult.progress && statusResult.progress.length > lastProgressLength) {
|
|
1725
|
+
const newMessages = statusResult.progress.slice(lastProgressLength);
|
|
1726
|
+
newMessages.forEach(msg => {
|
|
1727
|
+
output.textContent += msg + '\\n';
|
|
1728
|
+
});
|
|
1729
|
+
lastProgressLength = statusResult.progress.length;
|
|
1730
|
+
}
|
|
1731
|
+
} catch (e) {}
|
|
1732
|
+
}, 500);
|
|
1733
|
+
|
|
1734
|
+
try {
|
|
1735
|
+
const result = await api('/environments');
|
|
1736
|
+
clearInterval(pollInterval);
|
|
1737
|
+
|
|
1738
|
+
if (result.success) {
|
|
1739
|
+
detectedEnvironments = result.environments || [];
|
|
1740
|
+
|
|
1741
|
+
status.textContent = detectedEnvironments.length + ' found';
|
|
1742
|
+
status.className = 'status-badge status-success';
|
|
1743
|
+
loading.classList.add('hidden');
|
|
1744
|
+
content.classList.remove('hidden');
|
|
1745
|
+
|
|
1746
|
+
renderEnvironmentCards();
|
|
1747
|
+
} else {
|
|
1748
|
+
throw new Error(result.error);
|
|
1749
|
+
}
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
clearInterval(pollInterval);
|
|
1752
|
+
status.textContent = 'Error';
|
|
1753
|
+
status.className = 'status-badge status-error';
|
|
1754
|
+
output.textContent += '\\n❌ Error: ' + error.message;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// Render environment cards
|
|
1759
|
+
function renderEnvironmentCards() {
|
|
1760
|
+
const container = document.getElementById('env-cards');
|
|
1761
|
+
const noEnvsMessage = document.getElementById('no-envs-message');
|
|
1762
|
+
|
|
1763
|
+
container.innerHTML = '';
|
|
1764
|
+
|
|
1765
|
+
if (detectedEnvironments.length === 0) {
|
|
1766
|
+
noEnvsMessage.classList.remove('hidden');
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
noEnvsMessage.classList.add('hidden');
|
|
1771
|
+
|
|
1772
|
+
for (const env of detectedEnvironments) {
|
|
1773
|
+
const card = document.createElement('div');
|
|
1774
|
+
card.className = 'env-card';
|
|
1775
|
+
|
|
1776
|
+
const info = document.createElement('div');
|
|
1777
|
+
info.className = 'env-card-info';
|
|
1778
|
+
|
|
1779
|
+
const name = document.createElement('div');
|
|
1780
|
+
name.className = 'env-card-name';
|
|
1781
|
+
name.textContent = env.env;
|
|
1782
|
+
info.appendChild(name);
|
|
1783
|
+
|
|
1784
|
+
const stats = document.createElement('div');
|
|
1785
|
+
stats.className = 'env-card-stats';
|
|
1786
|
+
|
|
1787
|
+
const statItems = [
|
|
1788
|
+
{ icon: '🔧', label: 'Workers', count: env.workers.length },
|
|
1789
|
+
{ icon: '📊', label: 'D1', count: env.d1.length },
|
|
1790
|
+
{ icon: '🗄️', label: 'KV', count: env.kv.length },
|
|
1791
|
+
{ icon: '📨', label: 'Queues', count: env.queues.length },
|
|
1792
|
+
{ icon: '📁', label: 'R2', count: env.r2.length },
|
|
1793
|
+
];
|
|
1794
|
+
|
|
1795
|
+
for (const item of statItems) {
|
|
1796
|
+
if (item.count > 0) {
|
|
1797
|
+
const stat = document.createElement('span');
|
|
1798
|
+
stat.className = 'env-card-stat';
|
|
1799
|
+
stat.textContent = item.icon + ' ' + item.count + ' ' + item.label;
|
|
1800
|
+
stats.appendChild(stat);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
info.appendChild(stats);
|
|
1805
|
+
card.appendChild(info);
|
|
1806
|
+
|
|
1807
|
+
const actions = document.createElement('div');
|
|
1808
|
+
actions.className = 'env-card-actions';
|
|
1809
|
+
|
|
1810
|
+
const detailBtn = document.createElement('button');
|
|
1811
|
+
detailBtn.className = 'btn-info';
|
|
1812
|
+
detailBtn.textContent = '📋 Details';
|
|
1813
|
+
detailBtn.addEventListener('click', () => showEnvDetail(env));
|
|
1814
|
+
actions.appendChild(detailBtn);
|
|
1815
|
+
|
|
1816
|
+
card.appendChild(actions);
|
|
1817
|
+
container.appendChild(card);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// Show environment details
|
|
1822
|
+
function showEnvDetail(env) {
|
|
1823
|
+
selectedEnvForDetail = env;
|
|
1824
|
+
|
|
1825
|
+
document.getElementById('detail-env-name').textContent = env.env;
|
|
1826
|
+
|
|
1827
|
+
// Render resource lists with loading state
|
|
1828
|
+
renderResourceList('detail-workers-list', 'detail-workers-count', env.workers, 'name', 'worker');
|
|
1829
|
+
renderResourceList('detail-d1-list', 'detail-d1-count', env.d1, 'name', 'd1');
|
|
1830
|
+
renderResourceList('detail-kv-list', 'detail-kv-count', env.kv, 'name', 'kv');
|
|
1831
|
+
renderResourceList('detail-queues-list', 'detail-queues-count', env.queues, 'name', 'queue');
|
|
1832
|
+
renderResourceList('detail-r2-list', 'detail-r2-count', env.r2, 'name', 'r2');
|
|
1833
|
+
|
|
1834
|
+
// Hide empty sections
|
|
1835
|
+
document.getElementById('detail-queues-section').style.display = env.queues.length === 0 ? 'none' : 'block';
|
|
1836
|
+
document.getElementById('detail-r2-section').style.display = env.r2.length === 0 ? 'none' : 'block';
|
|
1837
|
+
|
|
1838
|
+
showSection('envDetail');
|
|
1839
|
+
|
|
1840
|
+
// Load details asynchronously
|
|
1841
|
+
loadResourceDetails(env);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
// Helper to render resource list
|
|
1845
|
+
function renderResourceList(listId, countId, resources, nameKey, resourceType) {
|
|
1846
|
+
const list = document.getElementById(listId);
|
|
1847
|
+
const count = document.getElementById(countId);
|
|
1848
|
+
|
|
1849
|
+
list.innerHTML = '';
|
|
1850
|
+
count.textContent = '(' + resources.length + ')';
|
|
1851
|
+
|
|
1852
|
+
if (resources.length === 0) {
|
|
1853
|
+
const empty = document.createElement('div');
|
|
1854
|
+
empty.className = 'resource-empty';
|
|
1855
|
+
empty.textContent = 'None';
|
|
1856
|
+
list.appendChild(empty);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
for (const resource of resources) {
|
|
1861
|
+
const item = document.createElement('div');
|
|
1862
|
+
item.className = 'resource-item';
|
|
1863
|
+
item.id = 'resource-' + resourceType + '-' + (resource.name || resource.title || '').replace(/[^a-zA-Z0-9-]/g, '_');
|
|
1864
|
+
|
|
1865
|
+
const nameDiv = document.createElement('div');
|
|
1866
|
+
nameDiv.className = 'resource-item-name';
|
|
1867
|
+
nameDiv.textContent = resource[nameKey] || resource.title || resource.id || 'Unknown';
|
|
1868
|
+
item.appendChild(nameDiv);
|
|
1869
|
+
|
|
1870
|
+
// Add loading placeholder for D1 and Workers
|
|
1871
|
+
if (resourceType === 'd1' || resourceType === 'worker') {
|
|
1872
|
+
const detailsDiv = document.createElement('div');
|
|
1873
|
+
detailsDiv.className = 'resource-item-details resource-item-loading';
|
|
1874
|
+
detailsDiv.textContent = 'Loading...';
|
|
1875
|
+
item.appendChild(detailsDiv);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
list.appendChild(item);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// Load resource details asynchronously
|
|
1883
|
+
async function loadResourceDetails(env) {
|
|
1884
|
+
// Load D1 and Worker details in parallel
|
|
1885
|
+
const d1Promises = env.d1.map(db => loadD1Details(db.name));
|
|
1886
|
+
const workerPromises = env.workers.map(w => loadWorkerDetails(w.name));
|
|
1887
|
+
|
|
1888
|
+
// Wait for all to complete (don't block on errors)
|
|
1889
|
+
await Promise.allSettled([...d1Promises, ...workerPromises]);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// Load D1 database details
|
|
1893
|
+
async function loadD1Details(name) {
|
|
1894
|
+
try {
|
|
1895
|
+
const result = await fetch('/api/d1/' + encodeURIComponent(name) + '/info').then(r => r.json());
|
|
1896
|
+
|
|
1897
|
+
const itemId = 'resource-d1-' + name.replace(/[^a-zA-Z0-9-]/g, '_');
|
|
1898
|
+
const item = document.getElementById(itemId);
|
|
1899
|
+
if (!item) return;
|
|
1900
|
+
|
|
1901
|
+
const detailsDiv = item.querySelector('.resource-item-details');
|
|
1902
|
+
if (!detailsDiv) return;
|
|
1903
|
+
|
|
1904
|
+
if (result.success && result.info) {
|
|
1905
|
+
const info = result.info;
|
|
1906
|
+
detailsDiv.className = 'resource-item-details';
|
|
1907
|
+
detailsDiv.innerHTML = '';
|
|
1908
|
+
|
|
1909
|
+
if (info.databaseSize) {
|
|
1910
|
+
const span = document.createElement('span');
|
|
1911
|
+
span.textContent = '📦 ' + info.databaseSize;
|
|
1912
|
+
detailsDiv.appendChild(span);
|
|
1913
|
+
}
|
|
1914
|
+
if (info.region) {
|
|
1915
|
+
const span = document.createElement('span');
|
|
1916
|
+
span.textContent = '🌍 ' + info.region;
|
|
1917
|
+
detailsDiv.appendChild(span);
|
|
1918
|
+
}
|
|
1919
|
+
if (info.createdAt) {
|
|
1920
|
+
const span = document.createElement('span');
|
|
1921
|
+
span.textContent = '📅 ' + formatDate(info.createdAt);
|
|
1922
|
+
detailsDiv.appendChild(span);
|
|
1923
|
+
}
|
|
1924
|
+
} else {
|
|
1925
|
+
detailsDiv.className = 'resource-item-details resource-item-error';
|
|
1926
|
+
detailsDiv.textContent = 'Failed to load';
|
|
1927
|
+
}
|
|
1928
|
+
} catch (e) {
|
|
1929
|
+
console.error('Failed to load D1 details:', e);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// Load Worker deployment details
|
|
1934
|
+
async function loadWorkerDetails(name) {
|
|
1935
|
+
try {
|
|
1936
|
+
const result = await fetch('/api/worker/' + encodeURIComponent(name) + '/deployments').then(r => r.json());
|
|
1937
|
+
|
|
1938
|
+
const itemId = 'resource-worker-' + name.replace(/[^a-zA-Z0-9-]/g, '_');
|
|
1939
|
+
const item = document.getElementById(itemId);
|
|
1940
|
+
if (!item) return;
|
|
1941
|
+
|
|
1942
|
+
const detailsDiv = item.querySelector('.resource-item-details');
|
|
1943
|
+
if (!detailsDiv) return;
|
|
1944
|
+
|
|
1945
|
+
if (result.success && result.deployments) {
|
|
1946
|
+
const info = result.deployments;
|
|
1947
|
+
detailsDiv.className = 'resource-item-details';
|
|
1948
|
+
detailsDiv.innerHTML = '';
|
|
1949
|
+
|
|
1950
|
+
if (!info.exists) {
|
|
1951
|
+
detailsDiv.className = 'resource-item-details resource-item-not-deployed';
|
|
1952
|
+
detailsDiv.textContent = '⚠️ Not deployed';
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
if (info.lastDeployedAt) {
|
|
1957
|
+
const span = document.createElement('span');
|
|
1958
|
+
span.textContent = '🚀 ' + formatDate(info.lastDeployedAt);
|
|
1959
|
+
detailsDiv.appendChild(span);
|
|
1960
|
+
}
|
|
1961
|
+
if (info.author) {
|
|
1962
|
+
const span = document.createElement('span');
|
|
1963
|
+
span.textContent = '👤 ' + info.author;
|
|
1964
|
+
detailsDiv.appendChild(span);
|
|
1965
|
+
}
|
|
1966
|
+
if (info.versionId) {
|
|
1967
|
+
const span = document.createElement('span');
|
|
1968
|
+
span.textContent = '🏷️ ' + info.versionId.substring(0, 8) + '...';
|
|
1969
|
+
detailsDiv.appendChild(span);
|
|
1970
|
+
}
|
|
1971
|
+
} else {
|
|
1972
|
+
detailsDiv.className = 'resource-item-details resource-item-not-deployed';
|
|
1973
|
+
detailsDiv.textContent = '⚠️ Not deployed';
|
|
1974
|
+
}
|
|
1975
|
+
} catch (e) {
|
|
1976
|
+
console.error('Failed to load Worker details:', e);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// Format ISO date to readable format with timezone
|
|
1981
|
+
function formatDate(isoString) {
|
|
1982
|
+
try {
|
|
1983
|
+
const date = new Date(isoString);
|
|
1984
|
+
const dateStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
1985
|
+
// Get timezone abbreviation
|
|
1986
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1987
|
+
const tzAbbr = date.toLocaleTimeString('en-US', { timeZoneName: 'short' }).split(' ').pop();
|
|
1988
|
+
return dateStr + ' (' + tzAbbr + ')';
|
|
1989
|
+
} catch {
|
|
1990
|
+
return isoString;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
// Show delete confirmation
|
|
1995
|
+
function showDeleteConfirmation(env) {
|
|
1996
|
+
selectedEnvForDelete = env;
|
|
1997
|
+
|
|
1998
|
+
document.getElementById('delete-env-name').textContent = env.env;
|
|
1999
|
+
document.getElementById('delete-workers-count').textContent = '(' + env.workers.length + ' workers)';
|
|
2000
|
+
document.getElementById('delete-d1-count').textContent = '(' + env.d1.length + ' databases)';
|
|
2001
|
+
document.getElementById('delete-kv-count').textContent = '(' + env.kv.length + ' namespaces)';
|
|
2002
|
+
document.getElementById('delete-queues-count').textContent = '(' + env.queues.length + ' queues)';
|
|
2003
|
+
document.getElementById('delete-r2-count').textContent = '(' + env.r2.length + ' buckets)';
|
|
2004
|
+
|
|
2005
|
+
// Reset checkboxes
|
|
2006
|
+
document.getElementById('delete-workers').checked = true;
|
|
2007
|
+
document.getElementById('delete-d1').checked = true;
|
|
2008
|
+
document.getElementById('delete-kv').checked = true;
|
|
2009
|
+
document.getElementById('delete-queues').checked = true;
|
|
2010
|
+
document.getElementById('delete-r2').checked = true;
|
|
2011
|
+
|
|
2012
|
+
// Reset UI state
|
|
2013
|
+
document.getElementById('delete-log').classList.add('hidden');
|
|
2014
|
+
document.getElementById('delete-result').classList.add('hidden');
|
|
2015
|
+
document.getElementById('delete-result').innerHTML = '';
|
|
2016
|
+
document.getElementById('btn-confirm-delete').disabled = false;
|
|
2017
|
+
|
|
2018
|
+
showSection('envDelete');
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
// Back buttons for environment management
|
|
2022
|
+
document.getElementById('btn-back-env-list').addEventListener('click', () => {
|
|
2023
|
+
showSection('topMenu');
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
document.getElementById('btn-refresh-env-list').addEventListener('click', () => {
|
|
2027
|
+
loadEnvironments();
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
document.getElementById('btn-back-env-detail').addEventListener('click', () => {
|
|
2031
|
+
showSection('envList');
|
|
2032
|
+
});
|
|
2033
|
+
|
|
2034
|
+
document.getElementById('btn-delete-from-detail').addEventListener('click', () => {
|
|
2035
|
+
if (selectedEnvForDetail) {
|
|
2036
|
+
showDeleteConfirmation(selectedEnvForDetail);
|
|
2037
|
+
}
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
document.getElementById('btn-back-env-delete').addEventListener('click', () => {
|
|
2041
|
+
// Go back to detail view if we came from there
|
|
2042
|
+
if (selectedEnvForDetail) {
|
|
2043
|
+
showSection('envDetail');
|
|
2044
|
+
} else {
|
|
2045
|
+
showSection('envList');
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
// Delete environment
|
|
2050
|
+
document.getElementById('btn-confirm-delete').addEventListener('click', async () => {
|
|
2051
|
+
if (!selectedEnvForDelete) return;
|
|
2052
|
+
|
|
2053
|
+
const btn = document.getElementById('btn-confirm-delete');
|
|
2054
|
+
const log = document.getElementById('delete-log');
|
|
2055
|
+
const output = document.getElementById('delete-output');
|
|
2056
|
+
const result = document.getElementById('delete-result');
|
|
2057
|
+
|
|
2058
|
+
btn.disabled = true;
|
|
2059
|
+
log.classList.remove('hidden');
|
|
2060
|
+
result.classList.add('hidden');
|
|
2061
|
+
output.textContent = '';
|
|
2062
|
+
|
|
2063
|
+
const deleteOptions = {
|
|
2064
|
+
deleteWorkers: document.getElementById('delete-workers').checked,
|
|
2065
|
+
deleteD1: document.getElementById('delete-d1').checked,
|
|
2066
|
+
deleteKV: document.getElementById('delete-kv').checked,
|
|
2067
|
+
deleteQueues: document.getElementById('delete-queues').checked,
|
|
2068
|
+
deleteR2: document.getElementById('delete-r2').checked,
|
|
2069
|
+
};
|
|
2070
|
+
|
|
2071
|
+
// Poll for progress
|
|
2072
|
+
let lastProgressLength = 0;
|
|
2073
|
+
const pollInterval = setInterval(async () => {
|
|
2074
|
+
try {
|
|
2075
|
+
const statusResult = await api('/deploy/status');
|
|
2076
|
+
if (statusResult.progress && statusResult.progress.length > lastProgressLength) {
|
|
2077
|
+
const newMessages = statusResult.progress.slice(lastProgressLength);
|
|
2078
|
+
newMessages.forEach(msg => {
|
|
2079
|
+
output.textContent += msg + '\\n';
|
|
2080
|
+
});
|
|
2081
|
+
lastProgressLength = statusResult.progress.length;
|
|
2082
|
+
log.scrollTop = log.scrollHeight;
|
|
2083
|
+
}
|
|
2084
|
+
} catch (e) {}
|
|
2085
|
+
}, 500);
|
|
2086
|
+
|
|
2087
|
+
try {
|
|
2088
|
+
const deleteResult = await api('/environments/' + selectedEnvForDelete.env + '/delete', {
|
|
2089
|
+
method: 'POST',
|
|
2090
|
+
body: deleteOptions,
|
|
2091
|
+
});
|
|
2092
|
+
|
|
2093
|
+
clearInterval(pollInterval);
|
|
2094
|
+
|
|
2095
|
+
// Show final progress
|
|
2096
|
+
if (deleteResult.progress) {
|
|
2097
|
+
output.textContent = deleteResult.progress.join('\\n');
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
result.classList.remove('hidden');
|
|
2101
|
+
|
|
2102
|
+
if (deleteResult.success) {
|
|
2103
|
+
result.innerHTML = '<div class="alert alert-success">✅ Environment deleted successfully!</div>';
|
|
2104
|
+
|
|
2105
|
+
// Refresh environment list after a short delay
|
|
2106
|
+
setTimeout(() => {
|
|
2107
|
+
loadEnvironments();
|
|
2108
|
+
showSection('envList');
|
|
2109
|
+
}, 2000);
|
|
2110
|
+
} else {
|
|
2111
|
+
result.innerHTML = '<div class="alert alert-error">❌ Some errors occurred: ' + (deleteResult.errors || []).join(', ') + '</div>';
|
|
2112
|
+
btn.disabled = false;
|
|
2113
|
+
}
|
|
2114
|
+
} catch (error) {
|
|
2115
|
+
clearInterval(pollInterval);
|
|
2116
|
+
result.classList.remove('hidden');
|
|
2117
|
+
result.innerHTML = '<div class="alert alert-error">❌ Error: ' + error.message + '</div>';
|
|
2118
|
+
btn.disabled = false;
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
|
|
1099
2122
|
// Initialize
|
|
1100
|
-
|
|
2123
|
+
if (MANAGE_ONLY) {
|
|
2124
|
+
// Skip prerequisites UI and go directly to environment management
|
|
2125
|
+
// Prerequisites were already checked by CLI
|
|
2126
|
+
loadEnvironments();
|
|
2127
|
+
showSection('envList');
|
|
2128
|
+
} else {
|
|
2129
|
+
checkPrerequisites();
|
|
2130
|
+
}
|
|
1101
2131
|
</script>
|
|
1102
2132
|
</body>
|
|
1103
2133
|
</html>`;
|