web-agent-bridge 3.2.0 → 3.4.0
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/LICENSE +84 -72
- package/README.ar.md +1304 -1152
- package/README.md +298 -1635
- package/bin/agent-runner.js +474 -474
- package/bin/cli.js +237 -138
- package/bin/wab-init.js +223 -0
- package/bin/wab.js +80 -80
- package/examples/azure-dns-wab.js +83 -0
- package/examples/bidi-agent.js +119 -119
- package/examples/cloudflare-wab-dns.js +121 -0
- package/examples/cpanel-wab-dns.js +114 -0
- package/examples/cross-site-agent.js +91 -91
- package/examples/dns-discovery-agent.js +166 -0
- package/examples/gcp-dns-wab.js +76 -0
- package/examples/governance-agent.js +169 -0
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -44
- package/examples/plesk-wab-dns.js +103 -0
- package/examples/puppeteer-agent.js +108 -108
- package/examples/route53-wab-dns.js +144 -0
- package/examples/saas-dashboard/README.md +55 -55
- package/examples/safe-mode-agent.js +96 -0
- package/examples/shopify-hydrogen/README.md +74 -74
- package/examples/vision-agent.js +171 -171
- package/examples/wab-sign.js +74 -0
- package/examples/wab-verify.js +60 -0
- package/examples/wordpress-elementor/README.md +77 -77
- package/package.json +19 -6
- package/public/.well-known/agent-tools.json +180 -180
- package/public/.well-known/ai-assets.json +59 -59
- package/public/.well-known/security.txt +8 -0
- package/public/.well-known/wab.json +28 -0
- package/public/activate.html +368 -0
- package/public/adoption-metrics.html +188 -0
- package/public/agent-workspace.html +349 -349
- package/public/ai.html +198 -198
- package/public/api.html +413 -412
- package/public/azure-dns-integration.html +289 -0
- package/public/browser.html +486 -486
- package/public/cloudflare-integration.html +380 -0
- package/public/commander-dashboard.html +243 -243
- package/public/cookies.html +210 -210
- package/public/cpanel-integration.html +398 -0
- package/public/css/agent-workspace.css +1713 -1713
- package/public/css/premium.css +317 -317
- package/public/css/styles.css +1263 -1235
- package/public/dashboard.html +707 -706
- package/public/dns.html +436 -0
- package/public/docs.html +588 -587
- package/public/feed.xml +89 -89
- package/public/gcp-dns-integration.html +318 -0
- package/public/growth.html +465 -463
- package/public/index.html +1266 -982
- package/public/integrations.html +556 -0
- package/public/js/activate.js +145 -0
- package/public/js/agent-workspace.js +1740 -1740
- package/public/js/auth-nav.js +65 -31
- package/public/js/auth-redirect.js +12 -12
- package/public/js/cookie-consent.js +56 -56
- package/public/js/dns.js +438 -0
- package/public/js/wab-demo-page.js +721 -721
- package/public/js/ws-client.js +74 -74
- package/public/llms-full.txt +360 -360
- package/public/llms.txt +125 -125
- package/public/login.html +85 -85
- package/public/mesh-dashboard.html +328 -328
- package/public/openapi.json +669 -580
- package/public/phone-shield.html +281 -0
- package/public/plesk-integration.html +375 -0
- package/public/premium-dashboard.html +2489 -2489
- package/public/premium.html +793 -793
- package/public/privacy.html +297 -297
- package/public/provider-onboarding.html +172 -0
- package/public/provider-sandbox.html +134 -0
- package/public/providers.html +359 -0
- package/public/register.html +105 -105
- package/public/registrar-integrations.html +141 -0
- package/public/robots.txt +99 -87
- package/public/route53-integration.html +531 -0
- package/public/script/wab-consent.d.ts +36 -36
- package/public/script/wab-consent.js +104 -104
- package/public/script/wab-schema.js +131 -131
- package/public/script/wab.d.ts +108 -108
- package/public/script/wab.min.js +580 -580
- package/public/security.txt +8 -0
- package/public/shieldqr.html +231 -0
- package/public/sitemap.xml +6 -0
- package/public/terms.html +256 -256
- package/public/wab-trust.html +200 -0
- package/public/wab-vs-protocols.html +210 -0
- package/public/whitepaper.html +449 -0
- package/script/ai-agent-bridge.js +1754 -1754
- package/sdk/README.md +99 -99
- package/sdk/agent-mesh.js +449 -449
- package/sdk/auto-discovery.js +288 -0
- package/sdk/commander.js +262 -262
- package/sdk/governance.js +262 -0
- package/sdk/index.d.ts +464 -464
- package/sdk/index.js +25 -1
- package/sdk/multi-agent.js +318 -318
- package/sdk/package.json +2 -2
- package/sdk/safe-mode.js +221 -0
- package/sdk/safety-shield.js +219 -0
- package/sdk/schema-discovery.js +83 -83
- package/server/adapters/index.js +520 -520
- package/server/config/plans.js +367 -367
- package/server/config/secrets.js +102 -102
- package/server/control-plane/index.js +301 -301
- package/server/data-plane/index.js +354 -354
- package/server/index.js +670 -427
- package/server/llm/index.js +404 -404
- package/server/middleware/adminAuth.js +35 -35
- package/server/middleware/auth.js +50 -50
- package/server/middleware/featureGate.js +88 -88
- package/server/middleware/rateLimits.js +100 -100
- package/server/middleware/sensitiveAction.js +157 -0
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/migrations/003_ads_integer_cents.sql +33 -33
- package/server/migrations/004_agent_os.sql +158 -158
- package/server/migrations/005_marketplace_metering.sql +126 -126
- package/server/migrations/007_governance.sql +106 -0
- package/server/migrations/008_plans.sql +144 -0
- package/server/migrations/009_shieldqr.sql +30 -0
- package/server/migrations/010_extended_trust.sql +33 -0
- package/server/models/adapters/index.js +33 -33
- package/server/models/adapters/mysql.js +183 -183
- package/server/models/adapters/postgresql.js +172 -172
- package/server/models/adapters/sqlite.js +7 -7
- package/server/models/db.js +740 -681
- package/server/observability/failure-analysis.js +337 -337
- package/server/observability/index.js +394 -394
- package/server/protocol/capabilities.js +223 -223
- package/server/protocol/index.js +243 -243
- package/server/protocol/schema.js +584 -584
- package/server/registry/certification.js +271 -271
- package/server/registry/index.js +326 -326
- package/server/routes/admin-plans.js +76 -0
- package/server/routes/admin-premium.js +673 -671
- package/server/routes/admin-shieldqr.js +90 -0
- package/server/routes/admin-trust-monitor.js +83 -0
- package/server/routes/admin.js +549 -261
- package/server/routes/ads.js +130 -130
- package/server/routes/agent-workspace.js +540 -540
- package/server/routes/api.js +150 -150
- package/server/routes/auth.js +71 -71
- package/server/routes/billing.js +57 -45
- package/server/routes/commander.js +316 -316
- package/server/routes/demo-showcase.js +332 -332
- package/server/routes/demo-store.js +154 -0
- package/server/routes/discovery.js +2348 -417
- package/server/routes/gateway.js +173 -157
- package/server/routes/governance.js +208 -0
- package/server/routes/license.js +251 -240
- package/server/routes/mesh.js +469 -469
- package/server/routes/noscript.js +543 -543
- package/server/routes/plans.js +33 -0
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/routes/providers.js +650 -0
- package/server/routes/runtime.js +2148 -2147
- package/server/routes/shieldqr.js +88 -0
- package/server/routes/sovereign.js +465 -385
- package/server/routes/universal.js +200 -185
- package/server/routes/wab-api.js +850 -501
- package/server/runtime/container-worker.js +111 -111
- package/server/runtime/container.js +448 -448
- package/server/runtime/distributed-worker.js +362 -362
- package/server/runtime/event-bus.js +210 -210
- package/server/runtime/index.js +253 -253
- package/server/runtime/queue.js +599 -599
- package/server/runtime/replay.js +666 -666
- package/server/runtime/sandbox.js +266 -266
- package/server/runtime/scheduler.js +534 -534
- package/server/runtime/session-engine.js +293 -293
- package/server/runtime/state-manager.js +188 -188
- package/server/security/cross-site-redactor.js +196 -0
- package/server/security/dry-run.js +180 -0
- package/server/security/human-gate-rate-limit.js +147 -0
- package/server/security/human-gate-transports.js +178 -0
- package/server/security/human-gate.js +281 -0
- package/server/security/index.js +368 -368
- package/server/security/intent-engine.js +245 -0
- package/server/security/reward-guard.js +171 -0
- package/server/security/rollback-store.js +239 -0
- package/server/security/token-scope.js +404 -0
- package/server/security/url-policy.js +139 -0
- package/server/services/agent-chat.js +506 -506
- package/server/services/agent-learning.js +601 -575
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +555 -539
- package/server/services/agent-symphony.js +717 -717
- package/server/services/agent-tasks.js +1807 -1807
- package/server/services/api-key-engine.js +292 -261
- package/server/services/cluster.js +894 -894
- package/server/services/commander.js +738 -738
- package/server/services/edge-compute.js +440 -440
- package/server/services/email.js +233 -204
- package/server/services/governance.js +466 -0
- package/server/services/hosted-runtime.js +205 -205
- package/server/services/lfd.js +635 -635
- package/server/services/local-ai.js +389 -389
- package/server/services/marketplace.js +270 -270
- package/server/services/metering.js +182 -182
- package/server/services/modules/affiliate-intelligence.js +93 -93
- package/server/services/modules/agent-firewall.js +90 -90
- package/server/services/modules/bounty.js +89 -89
- package/server/services/modules/collective-bargaining.js +92 -92
- package/server/services/modules/dark-pattern.js +66 -66
- package/server/services/modules/gov-intelligence.js +45 -45
- package/server/services/modules/neural.js +55 -55
- package/server/services/modules/notary.js +49 -49
- package/server/services/modules/price-time-machine.js +86 -86
- package/server/services/modules/protocol.js +104 -104
- package/server/services/negotiation.js +439 -439
- package/server/services/plans.js +214 -0
- package/server/services/plugins.js +771 -771
- package/server/services/premium.js +1 -1
- package/server/services/price-intelligence.js +566 -566
- package/server/services/price-shield.js +1137 -1137
- package/server/services/provider-clients.js +740 -0
- package/server/services/reputation.js +465 -465
- package/server/services/search-engine.js +357 -357
- package/server/services/security.js +513 -513
- package/server/services/self-healing.js +843 -843
- package/server/services/shieldqr.js +322 -0
- package/server/services/sovereign-shield.js +542 -0
- package/server/services/ssl-inspector.js +42 -0
- package/server/services/ssl-monitor.js +167 -0
- package/server/services/stripe.js +205 -192
- package/server/services/swarm.js +788 -788
- package/server/services/universal-scraper.js +662 -661
- package/server/services/verification.js +481 -481
- package/server/services/vision.js +1163 -1163
- package/server/services/wab-crypto.js +178 -0
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/safe-fetch.js +228 -0
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +161 -161
- package/templates/artisan-marketplace.yaml +104 -104
- package/templates/book-price-scout.yaml +98 -98
- package/templates/electronics-price-tracker.yaml +108 -108
- package/templates/flight-deal-hunter.yaml +113 -113
- package/templates/freelancer-direct.yaml +116 -116
- package/templates/grocery-price-compare.yaml +93 -93
- package/templates/hotel-direct-booking.yaml +113 -113
- package/templates/local-services.yaml +98 -98
- package/templates/olive-oil-tunisia.yaml +88 -88
- package/templates/organic-farm-fresh.yaml +101 -101
- package/templates/restaurant-direct.yaml +97 -97
- package/public/score.html +0 -263
- package/server/migrations/006_growth_suite.sql +0 -138
- package/server/routes/growth.js +0 -962
- package/server/services/fairness-engine.js +0 -409
- package/server/services/fairness.js +0 -420
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>DNS Providers — Web Agent Bridge</title>
|
|
7
|
+
<meta name="description" content="Connect Cloudflare, Route 53, Azure DNS, GCP, cPanel, Plesk, GoDaddy, or Namecheap and toggle WAB DNS Discovery on every domain you own — server-side, encrypted, audit-logged.">
|
|
8
|
+
<script>
|
|
9
|
+
(function () {
|
|
10
|
+
try { if (!localStorage.getItem('wab_token')) window.location.replace('/login'); }
|
|
11
|
+
catch (e) { window.location.replace('/login'); }
|
|
12
|
+
})();
|
|
13
|
+
</script>
|
|
14
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
15
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
16
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
17
|
+
<link rel="stylesheet" href="/css/styles.css">
|
|
18
|
+
<style>
|
|
19
|
+
body { background: #0b0f17; color: #e5e7eb; font-family: 'Inter', system-ui, sans-serif; margin: 0; }
|
|
20
|
+
.wrap { max-width: 1200px; margin: 0 auto; padding: 32px 24px; }
|
|
21
|
+
.topbar { display:flex; justify-content:space-between; align-items:center; padding-bottom:24px; border-bottom:1px solid #1f2937; margin-bottom:32px; }
|
|
22
|
+
.topbar h1 { font-size:1.6rem; margin:0; font-weight:700; }
|
|
23
|
+
.topbar .actions { display:flex; gap:12px; }
|
|
24
|
+
.btn { display:inline-block; padding:10px 18px; border-radius:8px; border:1px solid #374151; background:#1f2937; color:#e5e7eb; font-weight:600; cursor:pointer; font-size:0.92rem; text-decoration:none; transition:all 0.15s; }
|
|
25
|
+
.btn:hover { background:#374151; border-color:#4b5563; }
|
|
26
|
+
.btn-primary { background:linear-gradient(135deg,#3b82f6,#1d4ed8); border-color:transparent; color:#fff; }
|
|
27
|
+
.btn-primary:hover { filter:brightness(1.1); }
|
|
28
|
+
.btn-danger { background:#7f1d1d; border-color:#991b1b; color:#fee2e2; }
|
|
29
|
+
.btn-danger:hover { background:#991b1b; }
|
|
30
|
+
.btn-success { background:#065f46; border-color:#047857; color:#d1fae5; }
|
|
31
|
+
.btn-sm { padding:6px 12px; font-size:0.82rem; }
|
|
32
|
+
.grid { display:grid; gap:20px; }
|
|
33
|
+
.grid-2 { grid-template-columns: 1fr 1fr; }
|
|
34
|
+
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
|
35
|
+
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
|
36
|
+
@media(max-width:900px){ .grid-2,.grid-3,.grid-4{grid-template-columns:1fr;} }
|
|
37
|
+
.card { background:#111827; border:1px solid #1f2937; border-radius:12px; padding:24px; }
|
|
38
|
+
.card h2 { font-size:1.15rem; margin:0 0 16px; }
|
|
39
|
+
.stat { background:#111827; border:1px solid #1f2937; border-radius:12px; padding:18px; text-align:center; }
|
|
40
|
+
.stat .label { color:#94a3b8; font-size:0.78rem; text-transform:uppercase; letter-spacing:0.06em; }
|
|
41
|
+
.stat .value { font-size:1.8rem; font-weight:800; margin-top:6px; }
|
|
42
|
+
.provider-pick { border:1px solid #1f2937; border-radius:10px; padding:16px; cursor:pointer; transition:all 0.15s; background:#0f172a; }
|
|
43
|
+
.provider-pick:hover { border-color:#3b82f6; background:#111827; }
|
|
44
|
+
.provider-pick h3 { margin:0 0 4px; font-size:1rem; }
|
|
45
|
+
.provider-pick small { color:#94a3b8; }
|
|
46
|
+
table { width:100%; border-collapse:collapse; }
|
|
47
|
+
th, td { padding:10px 12px; text-align:left; border-bottom:1px solid #1f2937; font-size:0.92rem; }
|
|
48
|
+
th { color:#94a3b8; font-weight:600; font-size:0.78rem; text-transform:uppercase; letter-spacing:0.04em; }
|
|
49
|
+
.pill { display:inline-block; padding:3px 10px; border-radius:999px; font-size:0.75rem; font-weight:600; }
|
|
50
|
+
.pill-ok { background:#064e3b; color:#6ee7b7; }
|
|
51
|
+
.pill-err { background:#7f1d1d; color:#fecaca; }
|
|
52
|
+
.pill-pending { background:#374151; color:#cbd5e1; }
|
|
53
|
+
.form-group { margin-bottom:14px; }
|
|
54
|
+
.form-group label { display:block; margin-bottom:6px; font-size:0.85rem; color:#cbd5e1; font-weight:500; }
|
|
55
|
+
.form-group input, .form-group textarea, .form-group select {
|
|
56
|
+
width:100%; padding:10px 12px; background:#0f172a; border:1px solid #374151;
|
|
57
|
+
border-radius:8px; color:#e5e7eb; font-family:inherit; font-size:0.9rem;
|
|
58
|
+
}
|
|
59
|
+
.form-group input:focus, .form-group textarea:focus { outline:none; border-color:#3b82f6; }
|
|
60
|
+
.form-group small.help { display:block; color:#94a3b8; margin-top:4px; font-size:0.78rem; }
|
|
61
|
+
.modal { position:fixed; inset:0; background:rgba(0,0,0,0.7); display:none; align-items:center; justify-content:center; z-index:50; padding:24px; }
|
|
62
|
+
.modal.active { display:flex; }
|
|
63
|
+
.modal-content { background:#111827; border:1px solid #1f2937; border-radius:14px; padding:28px; max-width:560px; width:100%; max-height:85vh; overflow-y:auto; }
|
|
64
|
+
.modal-content h2 { margin-top:0; }
|
|
65
|
+
.alert { padding:12px 14px; border-radius:8px; margin-bottom:16px; font-size:0.9rem; }
|
|
66
|
+
.alert-ok { background:#064e3b; border:1px solid #047857; color:#d1fae5; }
|
|
67
|
+
.alert-err { background:#7f1d1d; border:1px solid #991b1b; color:#fecaca; }
|
|
68
|
+
.empty { text-align:center; color:#94a3b8; padding:40px 20px; }
|
|
69
|
+
code { background:#0f172a; padding:2px 6px; border-radius:4px; font-family:'JetBrains Mono', monospace; font-size:0.82rem; }
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<div class="wrap">
|
|
74
|
+
<div class="topbar">
|
|
75
|
+
<div>
|
|
76
|
+
<h1>🔗 DNS Providers</h1>
|
|
77
|
+
<div style="color:#94a3b8;font-size:0.9rem;margin-top:4px;">Connect your DNS provider to enable one-click WAB Discovery on every domain you own.</div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="actions">
|
|
80
|
+
<a href="/dashboard" class="btn">← Dashboard</a>
|
|
81
|
+
<button class="btn btn-primary" onclick="openProviderPicker()">+ Connect Provider</button>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div id="alertBox"></div>
|
|
86
|
+
|
|
87
|
+
<div class="grid grid-4" style="margin-bottom:32px;">
|
|
88
|
+
<div class="stat"><div class="label">Connected</div><div class="value" id="kAccounts">–</div></div>
|
|
89
|
+
<div class="stat"><div class="label">Active</div><div class="value" id="kActive" style="color:#4ade80">–</div></div>
|
|
90
|
+
<div class="stat"><div class="label">Domains</div><div class="value" id="kDomains">–</div></div>
|
|
91
|
+
<div class="stat"><div class="label">_wab Live</div><div class="value" id="kWab" style="color:#4ade80">–</div></div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="card" style="margin-bottom:32px;">
|
|
95
|
+
<h2>Your Connected Accounts</h2>
|
|
96
|
+
<div id="accountsList"></div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div class="card" id="domainsCard" style="display:none;">
|
|
100
|
+
<h2 id="domainsHeader">Domains</h2>
|
|
101
|
+
<div id="domainsList"></div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<!-- Provider picker modal -->
|
|
106
|
+
<div class="modal" id="pickerModal">
|
|
107
|
+
<div class="modal-content">
|
|
108
|
+
<h2>Choose your DNS provider</h2>
|
|
109
|
+
<div class="grid grid-2" id="pickerGrid"></div>
|
|
110
|
+
<div style="text-align:right; margin-top:16px;"><button class="btn" onclick="closeModal('pickerModal')">Cancel</button></div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- Connect form modal -->
|
|
115
|
+
<div class="modal" id="connectModal">
|
|
116
|
+
<div class="modal-content">
|
|
117
|
+
<h2 id="connectTitle">Connect Provider</h2>
|
|
118
|
+
<form id="connectForm" onsubmit="event.preventDefault(); submitConnect();">
|
|
119
|
+
<input type="hidden" id="connectType">
|
|
120
|
+
<div class="form-group">
|
|
121
|
+
<label>Label (your name for this account)</label>
|
|
122
|
+
<input type="text" id="connectLabel" placeholder="My Cloudflare account">
|
|
123
|
+
</div>
|
|
124
|
+
<div id="credFields"></div>
|
|
125
|
+
<div id="configFields"></div>
|
|
126
|
+
<div id="connectAlert"></div>
|
|
127
|
+
<div style="display:flex; gap:12px; justify-content:flex-end; margin-top:16px;">
|
|
128
|
+
<button type="button" class="btn" onclick="closeModal('connectModal')">Cancel</button>
|
|
129
|
+
<button type="submit" class="btn btn-primary" id="connectSubmit">Save & Test</button>
|
|
130
|
+
</div>
|
|
131
|
+
</form>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<script>
|
|
136
|
+
const API = '/api/providers';
|
|
137
|
+
const TOKEN = localStorage.getItem('wab_token');
|
|
138
|
+
let PROVIDER_TYPES = [];
|
|
139
|
+
let ACCOUNTS = [];
|
|
140
|
+
let SELECTED_ACCOUNT = null;
|
|
141
|
+
|
|
142
|
+
function H() { return { 'Content-Type': 'application/json', 'Authorization': `Bearer ${TOKEN}` }; }
|
|
143
|
+
function esc(s){ const d=document.createElement('div'); d.textContent=s==null?'':String(s); return d.innerHTML; }
|
|
144
|
+
function openModal(id){ document.getElementById(id).classList.add('active'); }
|
|
145
|
+
function closeModal(id){ document.getElementById(id).classList.remove('active'); }
|
|
146
|
+
function flash(msg, ok=true){
|
|
147
|
+
const el = document.getElementById('alertBox');
|
|
148
|
+
el.innerHTML = `<div class="alert ${ok?'alert-ok':'alert-err'}">${esc(msg)}</div>`;
|
|
149
|
+
setTimeout(() => { el.innerHTML = ''; }, 6000);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function loadTypes() {
|
|
153
|
+
const r = await fetch(`${API}/types`);
|
|
154
|
+
const j = await r.json();
|
|
155
|
+
PROVIDER_TYPES = j.providers || [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function loadAccounts() {
|
|
159
|
+
const r = await fetch(`${API}/accounts`, { headers: H() });
|
|
160
|
+
if (r.status === 401) { window.location = '/login'; return; }
|
|
161
|
+
const j = await r.json();
|
|
162
|
+
ACCOUNTS = j.accounts || [];
|
|
163
|
+
renderAccounts();
|
|
164
|
+
renderStats();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function renderStats() {
|
|
168
|
+
const total = ACCOUNTS.length;
|
|
169
|
+
const active = ACCOUNTS.filter(a => a.status === 'active').length;
|
|
170
|
+
const domains = ACCOUNTS.reduce((n, a) => n + (a.domains_count || 0), 0);
|
|
171
|
+
document.getElementById('kAccounts').textContent = total;
|
|
172
|
+
document.getElementById('kActive').textContent = active;
|
|
173
|
+
document.getElementById('kDomains').textContent = domains;
|
|
174
|
+
document.getElementById('kWab').textContent = '…';
|
|
175
|
+
// _wab live count comes from the currently-selected account view
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function statusPill(a) {
|
|
179
|
+
if (a.status === 'active') return '<span class="pill pill-ok">● active</span>';
|
|
180
|
+
if (a.status === 'error') return `<span class="pill pill-err">● error</span>`;
|
|
181
|
+
return '<span class="pill pill-pending">○ pending</span>';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function renderAccounts() {
|
|
185
|
+
const root = document.getElementById('accountsList');
|
|
186
|
+
if (!ACCOUNTS.length) {
|
|
187
|
+
root.innerHTML = '<div class="empty">You have not connected any DNS provider yet.<br><br><button class="btn btn-primary" onclick="openProviderPicker()">+ Connect your first provider</button></div>';
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
root.innerHTML = '<table><thead><tr><th>Provider</th><th>Label</th><th>Status</th><th>Domains</th><th>Last Test</th><th></th></tr></thead><tbody>' +
|
|
191
|
+
ACCOUNTS.map(a => {
|
|
192
|
+
const t = PROVIDER_TYPES.find(p => p.type === a.provider_type) || { label: a.provider_type };
|
|
193
|
+
const errMsg = a.last_test_error ? `<br><small style="color:#fca5a5">${esc(a.last_test_error)}</small>` : '';
|
|
194
|
+
return `<tr>
|
|
195
|
+
<td><strong>${esc(t.label)}</strong></td>
|
|
196
|
+
<td>${esc(a.label)}</td>
|
|
197
|
+
<td>${statusPill(a)}${errMsg}</td>
|
|
198
|
+
<td>${a.domains_count || 0}</td>
|
|
199
|
+
<td><small>${esc(a.last_test_at || 'never')}</small></td>
|
|
200
|
+
<td style="text-align:right; white-space:nowrap;">
|
|
201
|
+
<button class="btn btn-sm" onclick="testAccount('${a.id}')">Test</button>
|
|
202
|
+
<button class="btn btn-sm" onclick="syncAccount('${a.id}')">Sync</button>
|
|
203
|
+
<button class="btn btn-sm" onclick="viewDomains('${a.id}')">Domains</button>
|
|
204
|
+
<button class="btn btn-sm btn-danger" onclick="deleteAccount('${a.id}')">Delete</button>
|
|
205
|
+
</td>
|
|
206
|
+
</tr>`;
|
|
207
|
+
}).join('') + '</tbody></table>';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function openProviderPicker() {
|
|
211
|
+
const grid = document.getElementById('pickerGrid');
|
|
212
|
+
grid.innerHTML = PROVIDER_TYPES.map(p => `
|
|
213
|
+
<div class="provider-pick" onclick="openConnect('${p.type}')">
|
|
214
|
+
<h3>${esc(p.label)}</h3>
|
|
215
|
+
<small>${(p.credential_fields || []).map(f => esc(f.label)).join(' · ')}</small>
|
|
216
|
+
</div>`).join('');
|
|
217
|
+
openModal('pickerModal');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function openConnect(type) {
|
|
221
|
+
closeModal('pickerModal');
|
|
222
|
+
const p = PROVIDER_TYPES.find(x => x.type === type);
|
|
223
|
+
if (!p) return;
|
|
224
|
+
document.getElementById('connectType').value = type;
|
|
225
|
+
document.getElementById('connectTitle').textContent = `Connect ${p.label}`;
|
|
226
|
+
document.getElementById('connectLabel').value = p.label;
|
|
227
|
+
document.getElementById('connectAlert').innerHTML = '';
|
|
228
|
+
document.getElementById('credFields').innerHTML = (p.credential_fields || []).map(renderField).join('');
|
|
229
|
+
document.getElementById('configFields').innerHTML = (p.config_fields || []).length
|
|
230
|
+
? '<hr style="border-color:#1f2937;margin:16px 0"><h3 style="font-size:0.95rem;color:#94a3b8;margin:0 0 12px">Configuration</h3>' + (p.config_fields).map(renderField).join('')
|
|
231
|
+
: '';
|
|
232
|
+
openModal('connectModal');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function renderField(f) {
|
|
236
|
+
const id = `f_${f.key}`;
|
|
237
|
+
const required = f.required ? 'required' : '';
|
|
238
|
+
const help = f.help ? `<small class="help">${esc(f.help)}</small>` : '';
|
|
239
|
+
if (f.type === 'textarea') {
|
|
240
|
+
return `<div class="form-group"><label>${esc(f.label)}${f.required?' *':''}</label><textarea id="${id}" name="${esc(f.key)}" rows="6" ${required}></textarea>${help}</div>`;
|
|
241
|
+
}
|
|
242
|
+
const t = f.type === 'password' ? 'password' : 'text';
|
|
243
|
+
return `<div class="form-group"><label>${esc(f.label)}${f.required?' *':''}</label><input type="${t}" id="${id}" name="${esc(f.key)}" ${required}>${help}</div>`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function submitConnect() {
|
|
247
|
+
const type = document.getElementById('connectType').value;
|
|
248
|
+
const p = PROVIDER_TYPES.find(x => x.type === type);
|
|
249
|
+
const credentials = {};
|
|
250
|
+
const config = {};
|
|
251
|
+
for (const f of (p.credential_fields || [])) credentials[f.key] = document.getElementById(`f_${f.key}`).value.trim();
|
|
252
|
+
for (const f of (p.config_fields || [])) config[f.key] = document.getElementById(`f_${f.key}`).value.trim();
|
|
253
|
+
const label = document.getElementById('connectLabel').value.trim() || p.label;
|
|
254
|
+
const alertEl = document.getElementById('connectAlert');
|
|
255
|
+
const submitBtn = document.getElementById('connectSubmit');
|
|
256
|
+
submitBtn.disabled = true; submitBtn.textContent = 'Saving…';
|
|
257
|
+
alertEl.innerHTML = '';
|
|
258
|
+
try {
|
|
259
|
+
const r = await fetch(`${API}/accounts`, {
|
|
260
|
+
method: 'POST', headers: H(),
|
|
261
|
+
body: JSON.stringify({ provider_type: type, label, credentials, config })
|
|
262
|
+
});
|
|
263
|
+
const j = await r.json();
|
|
264
|
+
if (!r.ok) throw new Error(j.error || 'Failed to save');
|
|
265
|
+
alertEl.innerHTML = '<div class="alert alert-ok">Saved. Testing connection…</div>';
|
|
266
|
+
const t = await fetch(`${API}/accounts/${j.account.id}/test`, { method: 'POST', headers: H() });
|
|
267
|
+
const tj = await t.json();
|
|
268
|
+
if (!t.ok) {
|
|
269
|
+
alertEl.innerHTML = `<div class="alert alert-err">Saved, but test failed: ${esc(tj.error)}</div>`;
|
|
270
|
+
} else {
|
|
271
|
+
alertEl.innerHTML = `<div class="alert alert-ok">✅ Connected (${esc(tj.detail)}). Syncing zones…</div>`;
|
|
272
|
+
await fetch(`${API}/accounts/${j.account.id}/sync`, { method: 'POST', headers: H() });
|
|
273
|
+
setTimeout(() => { closeModal('connectModal'); flash('Provider connected and synced.'); loadAccounts(); }, 600);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
} catch (e) {
|
|
277
|
+
alertEl.innerHTML = `<div class="alert alert-err">${esc(e.message)}</div>`;
|
|
278
|
+
} finally {
|
|
279
|
+
submitBtn.disabled = false; submitBtn.textContent = 'Save & Test';
|
|
280
|
+
}
|
|
281
|
+
loadAccounts();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function testAccount(id) {
|
|
285
|
+
flash('Testing…');
|
|
286
|
+
const r = await fetch(`${API}/accounts/${id}/test`, { method: 'POST', headers: H() });
|
|
287
|
+
const j = await r.json();
|
|
288
|
+
flash(r.ok ? `✅ ${j.detail || 'OK'}` : `❌ ${j.error}`, r.ok);
|
|
289
|
+
loadAccounts();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function syncAccount(id) {
|
|
293
|
+
flash('Syncing zones…');
|
|
294
|
+
const r = await fetch(`${API}/accounts/${id}/sync`, { method: 'POST', headers: H() });
|
|
295
|
+
const j = await r.json();
|
|
296
|
+
flash(r.ok ? `✅ Synced ${j.count} domains` : `❌ ${j.error}`, r.ok);
|
|
297
|
+
loadAccounts();
|
|
298
|
+
if (SELECTED_ACCOUNT === id) viewDomains(id);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function deleteAccount(id) {
|
|
302
|
+
if (!confirm('Delete this provider account? Stored credentials will be erased. This will not remove DNS records you already published.')) return;
|
|
303
|
+
const r = await fetch(`${API}/accounts/${id}`, { method: 'DELETE', headers: H() });
|
|
304
|
+
flash(r.ok ? 'Account deleted.' : 'Delete failed.', r.ok);
|
|
305
|
+
if (SELECTED_ACCOUNT === id) {
|
|
306
|
+
SELECTED_ACCOUNT = null;
|
|
307
|
+
document.getElementById('domainsCard').style.display = 'none';
|
|
308
|
+
}
|
|
309
|
+
loadAccounts();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function viewDomains(id) {
|
|
313
|
+
SELECTED_ACCOUNT = id;
|
|
314
|
+
const acc = ACCOUNTS.find(a => a.id === id);
|
|
315
|
+
const card = document.getElementById('domainsCard');
|
|
316
|
+
card.style.display = 'block';
|
|
317
|
+
const t = PROVIDER_TYPES.find(p => p.type === acc.provider_type) || { label: acc.provider_type };
|
|
318
|
+
document.getElementById('domainsHeader').textContent = `Domains — ${t.label} · ${acc.label}`;
|
|
319
|
+
document.getElementById('domainsList').innerHTML = '<div class="empty">Loading…</div>';
|
|
320
|
+
const r = await fetch(`${API}/accounts/${id}/domains`, { headers: H() });
|
|
321
|
+
const j = await r.json();
|
|
322
|
+
const domains = j.domains || [];
|
|
323
|
+
const wabLive = domains.filter(d => d.wab_enabled).length;
|
|
324
|
+
document.getElementById('kWab').textContent = wabLive;
|
|
325
|
+
if (!domains.length) {
|
|
326
|
+
document.getElementById('domainsList').innerHTML = '<div class="empty">No domains synced yet. Click <strong>Sync</strong> on this account to fetch them from your provider.</div>';
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
document.getElementById('domainsList').innerHTML = '<table><thead><tr><th>Domain</th><th>WAB</th><th>Last Action</th><th></th></tr></thead><tbody>' +
|
|
330
|
+
domains.map(d => `<tr>
|
|
331
|
+
<td><strong>${esc(d.domain)}</strong>${d.zone_id ? `<br><small style="color:#94a3b8">zone: <code>${esc(d.zone_id)}</code></small>` : ''}</td>
|
|
332
|
+
<td>${d.wab_enabled ? '<span class="pill pill-ok">● ON</span>' : '<span class="pill pill-pending">○ OFF</span>'}</td>
|
|
333
|
+
<td>${d.last_action ? `${esc(d.last_action)} · <span style="color:${d.last_action_status==='ok'?'#6ee7b7':'#fca5a5'}">${esc(d.last_action_status||'—')}</span><br><small style="color:#94a3b8">${esc(d.last_action_at||'')}</small>${d.last_action_error?'<br><small style="color:#fca5a5">'+esc(d.last_action_error)+'</small>':''}` : '<small style="color:#94a3b8">—</small>'}</td>
|
|
334
|
+
<td style="text-align:right;white-space:nowrap;">
|
|
335
|
+
${d.wab_enabled
|
|
336
|
+
? `<button class="btn btn-sm btn-danger" onclick="toggleWab('${id}','${esc(d.domain)}',false)">Disable WAB</button>`
|
|
337
|
+
: `<button class="btn btn-sm btn-success" onclick="toggleWab('${id}','${esc(d.domain)}',true)">Enable WAB</button>`}
|
|
338
|
+
</td>
|
|
339
|
+
</tr>`).join('') + '</tbody></table>';
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function toggleWab(accountId, domain, on) {
|
|
343
|
+
const path = on ? 'enable-wab' : 'disable-wab';
|
|
344
|
+
flash(`${on?'Enabling':'Disabling'} WAB on ${domain}…`);
|
|
345
|
+
const r = await fetch(`${API}/accounts/${accountId}/domains/${encodeURIComponent(domain)}/${path}`, {
|
|
346
|
+
method: 'POST', headers: H()
|
|
347
|
+
});
|
|
348
|
+
const j = await r.json();
|
|
349
|
+
flash(r.ok ? `✅ ${domain}: ${j.detail || 'ok'}` : `❌ ${domain}: ${j.error}`, r.ok);
|
|
350
|
+
viewDomains(accountId);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
(async function init() {
|
|
354
|
+
await loadTypes();
|
|
355
|
+
await loadAccounts();
|
|
356
|
+
})();
|
|
357
|
+
</script>
|
|
358
|
+
</body>
|
|
359
|
+
</html>
|
package/public/register.html
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Create Account — Web Agent Bridge</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
-
<style>body{background:#0a0e1a;color:#f0f4ff;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;margin:0;min-height:100vh}</style>
|
|
10
|
-
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
11
|
-
<noscript><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"></noscript>
|
|
12
|
-
<link rel="stylesheet" href="/css/styles.css?v=3.0.1">
|
|
13
|
-
<script src="/js/auth-redirect.js?v=3.0.1"></script>
|
|
14
|
-
</head>
|
|
15
|
-
<body>
|
|
16
|
-
<div class="auth-page">
|
|
17
|
-
<div class="auth-card fade-in">
|
|
18
|
-
<div style="text-align:center; margin-bottom:32px;">
|
|
19
|
-
<a href="/" class="navbar-brand" style="justify-content:center;">
|
|
20
|
-
<div class="brand-icon">⚡</div>
|
|
21
|
-
<span>WAB</span>
|
|
22
|
-
</a>
|
|
23
|
-
</div>
|
|
24
|
-
<h1 style="text-align:center;">Create account</h1>
|
|
25
|
-
<p class="subtitle" style="text-align:center;">Get started with Web Agent Bridge</p>
|
|
26
|
-
|
|
27
|
-
<div class="alert alert-error" id="errorAlert"></div>
|
|
28
|
-
<div class="alert alert-success" id="successAlert"></div>
|
|
29
|
-
|
|
30
|
-
<form id="registerForm">
|
|
31
|
-
<div class="form-group">
|
|
32
|
-
<label for="name">Full Name</label>
|
|
33
|
-
<input type="text" id="name" class="form-input" placeholder="John Doe" required>
|
|
34
|
-
</div>
|
|
35
|
-
<div class="form-group">
|
|
36
|
-
<label for="email">Email</label>
|
|
37
|
-
<input type="email" id="email" class="form-input" placeholder="you@example.com" required>
|
|
38
|
-
</div>
|
|
39
|
-
<div class="form-group">
|
|
40
|
-
<label for="company">Company <span style="color:var(--text-muted);">(optional)</span></label>
|
|
41
|
-
<input type="text" id="company" class="form-input" placeholder="Your company">
|
|
42
|
-
</div>
|
|
43
|
-
<div class="form-group">
|
|
44
|
-
<label for="password">Password</label>
|
|
45
|
-
<input type="password" id="password" class="form-input" placeholder="Min 8 characters" required minlength="8">
|
|
46
|
-
</div>
|
|
47
|
-
<div class="form-group" style="display: flex; align-items: flex-start; gap: 8px;">
|
|
48
|
-
<input type="checkbox" id="termsConsent" required style="margin-top: 4px; accent-color: var(--accent-blue);">
|
|
49
|
-
<label for="termsConsent" style="font-size: 0.85rem; color: var(--text-muted); line-height: 1.5;">
|
|
50
|
-
I agree to the <a href="/terms" style="color: var(--accent-blue);">Terms of Service</a> and
|
|
51
|
-
<a href="/privacy" style="color: var(--accent-blue);">Privacy Policy</a>
|
|
52
|
-
</label>
|
|
53
|
-
</div>
|
|
54
|
-
<button type="submit" class="btn btn-primary btn-lg">Create Account</button>
|
|
55
|
-
</form>
|
|
56
|
-
|
|
57
|
-
<div class="auth-footer">
|
|
58
|
-
Already have an account? <a href="/login">Sign in</a>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<script>
|
|
64
|
-
if (localStorage.getItem('wab_token')) {
|
|
65
|
-
window.location.replace(typeof wabGetPostAuthRedirect === 'function' ? wabGetPostAuthRedirect() : '/dashboard');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
document.getElementById('registerForm').addEventListener('submit', async (e) => {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
const errorEl = document.getElementById('errorAlert');
|
|
71
|
-
const successEl = document.getElementById('successAlert');
|
|
72
|
-
errorEl.style.display = 'none';
|
|
73
|
-
successEl.style.display = 'none';
|
|
74
|
-
|
|
75
|
-
const name = document.getElementById('name').value;
|
|
76
|
-
const email = document.getElementById('email').value;
|
|
77
|
-
const company = document.getElementById('company').value;
|
|
78
|
-
const password = document.getElementById('password').value;
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const res = await fetch('/api/auth/register', {
|
|
82
|
-
method: 'POST',
|
|
83
|
-
headers: { 'Content-Type': 'application/json' },
|
|
84
|
-
body: JSON.stringify({ name, email, company, password })
|
|
85
|
-
});
|
|
86
|
-
const data = await res.json();
|
|
87
|
-
|
|
88
|
-
if (!res.ok) {
|
|
89
|
-
errorEl.textContent = data.error || 'Registration failed';
|
|
90
|
-
errorEl.style.display = 'block';
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
localStorage.setItem('wab_token', data.token);
|
|
95
|
-
localStorage.setItem('wab_user', JSON.stringify(data.user));
|
|
96
|
-
window.location.href = typeof wabGetPostAuthRedirect === 'function' ? wabGetPostAuthRedirect() : '/dashboard';
|
|
97
|
-
} catch (err) {
|
|
98
|
-
errorEl.textContent = 'Connection error. Please try again.';
|
|
99
|
-
errorEl.style.display = 'block';
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
</script>
|
|
103
|
-
<script src="/js/cookie-consent.js?v=3.0.1"></script>
|
|
104
|
-
</body>
|
|
105
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Create Account — Web Agent Bridge</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<style>body{background:#0a0e1a;color:#f0f4ff;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;margin:0;min-height:100vh}</style>
|
|
10
|
+
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
11
|
+
<noscript><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"></noscript>
|
|
12
|
+
<link rel="stylesheet" href="/css/styles.css?v=3.0.1">
|
|
13
|
+
<script src="/js/auth-redirect.js?v=3.0.1"></script>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div class="auth-page">
|
|
17
|
+
<div class="auth-card fade-in">
|
|
18
|
+
<div style="text-align:center; margin-bottom:32px;">
|
|
19
|
+
<a href="/" class="navbar-brand" style="justify-content:center;">
|
|
20
|
+
<div class="brand-icon">⚡</div>
|
|
21
|
+
<span>WAB</span>
|
|
22
|
+
</a>
|
|
23
|
+
</div>
|
|
24
|
+
<h1 style="text-align:center;">Create account</h1>
|
|
25
|
+
<p class="subtitle" style="text-align:center;">Get started with Web Agent Bridge</p>
|
|
26
|
+
|
|
27
|
+
<div class="alert alert-error" id="errorAlert"></div>
|
|
28
|
+
<div class="alert alert-success" id="successAlert"></div>
|
|
29
|
+
|
|
30
|
+
<form id="registerForm">
|
|
31
|
+
<div class="form-group">
|
|
32
|
+
<label for="name">Full Name</label>
|
|
33
|
+
<input type="text" id="name" class="form-input" placeholder="John Doe" required>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="form-group">
|
|
36
|
+
<label for="email">Email</label>
|
|
37
|
+
<input type="email" id="email" class="form-input" placeholder="you@example.com" required>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="form-group">
|
|
40
|
+
<label for="company">Company <span style="color:var(--text-muted);">(optional)</span></label>
|
|
41
|
+
<input type="text" id="company" class="form-input" placeholder="Your company">
|
|
42
|
+
</div>
|
|
43
|
+
<div class="form-group">
|
|
44
|
+
<label for="password">Password</label>
|
|
45
|
+
<input type="password" id="password" class="form-input" placeholder="Min 8 characters" required minlength="8">
|
|
46
|
+
</div>
|
|
47
|
+
<div class="form-group" style="display: flex; align-items: flex-start; gap: 8px;">
|
|
48
|
+
<input type="checkbox" id="termsConsent" required style="margin-top: 4px; accent-color: var(--accent-blue);">
|
|
49
|
+
<label for="termsConsent" style="font-size: 0.85rem; color: var(--text-muted); line-height: 1.5;">
|
|
50
|
+
I agree to the <a href="/terms" style="color: var(--accent-blue);">Terms of Service</a> and
|
|
51
|
+
<a href="/privacy" style="color: var(--accent-blue);">Privacy Policy</a>
|
|
52
|
+
</label>
|
|
53
|
+
</div>
|
|
54
|
+
<button type="submit" class="btn btn-primary btn-lg">Create Account</button>
|
|
55
|
+
</form>
|
|
56
|
+
|
|
57
|
+
<div class="auth-footer">
|
|
58
|
+
Already have an account? <a href="/login">Sign in</a>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<script>
|
|
64
|
+
if (localStorage.getItem('wab_token')) {
|
|
65
|
+
window.location.replace(typeof wabGetPostAuthRedirect === 'function' ? wabGetPostAuthRedirect() : '/dashboard');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
document.getElementById('registerForm').addEventListener('submit', async (e) => {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
const errorEl = document.getElementById('errorAlert');
|
|
71
|
+
const successEl = document.getElementById('successAlert');
|
|
72
|
+
errorEl.style.display = 'none';
|
|
73
|
+
successEl.style.display = 'none';
|
|
74
|
+
|
|
75
|
+
const name = document.getElementById('name').value;
|
|
76
|
+
const email = document.getElementById('email').value;
|
|
77
|
+
const company = document.getElementById('company').value;
|
|
78
|
+
const password = document.getElementById('password').value;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const res = await fetch('/api/auth/register', {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: { 'Content-Type': 'application/json' },
|
|
84
|
+
body: JSON.stringify({ name, email, company, password })
|
|
85
|
+
});
|
|
86
|
+
const data = await res.json();
|
|
87
|
+
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
errorEl.textContent = data.error || 'Registration failed';
|
|
90
|
+
errorEl.style.display = 'block';
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
localStorage.setItem('wab_token', data.token);
|
|
95
|
+
localStorage.setItem('wab_user', JSON.stringify(data.user));
|
|
96
|
+
window.location.href = typeof wabGetPostAuthRedirect === 'function' ? wabGetPostAuthRedirect() : '/dashboard';
|
|
97
|
+
} catch (err) {
|
|
98
|
+
errorEl.textContent = 'Connection error. Please try again.';
|
|
99
|
+
errorEl.style.display = 'block';
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
</script>
|
|
103
|
+
<script src="/js/cookie-consent.js?v=3.0.1"></script>
|
|
104
|
+
</body>
|
|
105
|
+
</html>
|