web-agent-bridge 3.0.0 → 3.3.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.
Files changed (202) hide show
  1. package/LICENSE +72 -21
  2. package/README.ar.md +1286 -1073
  3. package/README.md +1764 -1535
  4. package/bin/agent-runner.js +474 -474
  5. package/bin/cli.js +237 -138
  6. package/bin/wab.js +80 -80
  7. package/examples/bidi-agent.js +119 -119
  8. package/examples/cross-site-agent.js +91 -91
  9. package/examples/mcp-agent.js +94 -94
  10. package/examples/next-app-router/README.md +44 -44
  11. package/examples/puppeteer-agent.js +108 -108
  12. package/examples/saas-dashboard/README.md +55 -55
  13. package/examples/shopify-hydrogen/README.md +74 -74
  14. package/examples/vision-agent.js +171 -171
  15. package/examples/wordpress-elementor/README.md +77 -77
  16. package/package.json +17 -3
  17. package/public/.well-known/agent-tools.json +180 -180
  18. package/public/.well-known/ai-assets.json +59 -59
  19. package/public/.well-known/ai-plugin.json +28 -0
  20. package/public/.well-known/security.txt +8 -0
  21. package/public/agent-workspace.html +349 -347
  22. package/public/ai.html +198 -196
  23. package/public/api.html +413 -0
  24. package/public/browser.html +486 -484
  25. package/public/commander-dashboard.html +243 -243
  26. package/public/cookies.html +210 -208
  27. package/public/css/agent-workspace.css +1713 -1713
  28. package/public/css/premium.css +317 -317
  29. package/public/css/styles.css +1235 -1235
  30. package/public/dashboard.html +706 -704
  31. package/public/demo.html +1770 -1
  32. package/public/dns.html +507 -0
  33. package/public/docs.html +587 -585
  34. package/public/feed.xml +89 -89
  35. package/public/growth.html +463 -0
  36. package/public/index.html +341 -9
  37. package/public/integrations.html +556 -0
  38. package/public/js/agent-workspace.js +1740 -1740
  39. package/public/js/auth-nav.js +31 -31
  40. package/public/js/auth-redirect.js +12 -12
  41. package/public/js/cookie-consent.js +56 -56
  42. package/public/js/wab-demo-page.js +721 -721
  43. package/public/js/ws-client.js +74 -74
  44. package/public/llms-full.txt +360 -309
  45. package/public/llms.txt +125 -86
  46. package/public/login.html +85 -83
  47. package/public/mesh-dashboard.html +328 -328
  48. package/public/openapi.json +580 -580
  49. package/public/phone-shield.html +281 -0
  50. package/public/premium-dashboard.html +2489 -2487
  51. package/public/premium.html +793 -791
  52. package/public/privacy.html +297 -295
  53. package/public/register.html +105 -103
  54. package/public/robots.txt +87 -87
  55. package/public/script/wab-consent.d.ts +36 -36
  56. package/public/script/wab-consent.js +104 -104
  57. package/public/script/wab-schema.js +131 -131
  58. package/public/script/wab.d.ts +108 -108
  59. package/public/script/wab.min.js +580 -580
  60. package/public/security.txt +8 -0
  61. package/public/terms.html +256 -254
  62. package/script/ai-agent-bridge.js +1754 -1754
  63. package/sdk/README.md +99 -99
  64. package/sdk/agent-mesh.js +449 -449
  65. package/sdk/commander.js +262 -262
  66. package/sdk/index.d.ts +464 -464
  67. package/sdk/index.js +18 -1
  68. package/sdk/multi-agent.js +318 -318
  69. package/sdk/package.json +12 -1
  70. package/sdk/safety-shield.js +219 -0
  71. package/sdk/schema-discovery.js +83 -83
  72. package/server/adapters/index.js +520 -520
  73. package/server/config/plans.js +367 -367
  74. package/server/config/secrets.js +102 -102
  75. package/server/control-plane/index.js +301 -301
  76. package/server/data-plane/index.js +354 -354
  77. package/server/index.js +175 -19
  78. package/server/llm/index.js +404 -404
  79. package/server/middleware/adminAuth.js +35 -35
  80. package/server/middleware/auth.js +50 -50
  81. package/server/middleware/featureGate.js +88 -88
  82. package/server/middleware/rateLimits.js +100 -100
  83. package/server/middleware/sensitiveAction.js +157 -0
  84. package/server/migrations/001_add_analytics_indexes.sql +7 -7
  85. package/server/migrations/002_premium_features.sql +418 -418
  86. package/server/migrations/003_ads_integer_cents.sql +33 -33
  87. package/server/migrations/004_agent_os.sql +158 -158
  88. package/server/migrations/005_marketplace_metering.sql +126 -126
  89. package/server/models/adapters/index.js +33 -33
  90. package/server/models/adapters/mysql.js +183 -183
  91. package/server/models/adapters/postgresql.js +172 -172
  92. package/server/models/adapters/sqlite.js +7 -7
  93. package/server/models/db.js +681 -681
  94. package/server/observability/failure-analysis.js +337 -337
  95. package/server/observability/index.js +394 -394
  96. package/server/protocol/capabilities.js +223 -223
  97. package/server/protocol/index.js +243 -243
  98. package/server/protocol/schema.js +584 -584
  99. package/server/registry/certification.js +271 -271
  100. package/server/registry/index.js +326 -326
  101. package/server/routes/admin-premium.js +671 -671
  102. package/server/routes/admin.js +261 -261
  103. package/server/routes/ads.js +130 -130
  104. package/server/routes/agent-workspace.js +540 -378
  105. package/server/routes/api.js +150 -150
  106. package/server/routes/auth.js +71 -71
  107. package/server/routes/billing.js +45 -45
  108. package/server/routes/commander.js +316 -316
  109. package/server/routes/demo-showcase.js +332 -0
  110. package/server/routes/demo-store.js +154 -0
  111. package/server/routes/discovery.js +417 -406
  112. package/server/routes/gateway.js +173 -0
  113. package/server/routes/license.js +251 -240
  114. package/server/routes/mesh.js +469 -469
  115. package/server/routes/noscript.js +543 -543
  116. package/server/routes/premium-v2.js +686 -686
  117. package/server/routes/premium.js +724 -724
  118. package/server/routes/runtime.js +2148 -2147
  119. package/server/routes/sovereign.js +465 -385
  120. package/server/routes/universal.js +200 -177
  121. package/server/routes/wab-api.js +850 -491
  122. package/server/runtime/container-worker.js +111 -111
  123. package/server/runtime/container.js +448 -448
  124. package/server/runtime/distributed-worker.js +362 -362
  125. package/server/runtime/event-bus.js +210 -210
  126. package/server/runtime/index.js +253 -253
  127. package/server/runtime/queue.js +599 -599
  128. package/server/runtime/replay.js +666 -666
  129. package/server/runtime/sandbox.js +266 -266
  130. package/server/runtime/scheduler.js +534 -534
  131. package/server/runtime/session-engine.js +293 -293
  132. package/server/runtime/state-manager.js +188 -188
  133. package/server/security/cross-site-redactor.js +196 -0
  134. package/server/security/dry-run.js +180 -0
  135. package/server/security/human-gate-rate-limit.js +147 -0
  136. package/server/security/human-gate-transports.js +178 -0
  137. package/server/security/human-gate.js +281 -0
  138. package/server/security/index.js +368 -368
  139. package/server/security/intent-engine.js +245 -0
  140. package/server/security/reward-guard.js +171 -0
  141. package/server/security/rollback-store.js +239 -0
  142. package/server/security/token-scope.js +404 -0
  143. package/server/security/url-policy.js +139 -0
  144. package/server/services/agent-chat.js +506 -506
  145. package/server/services/agent-learning.js +601 -575
  146. package/server/services/agent-memory.js +625 -625
  147. package/server/services/agent-mesh.js +555 -539
  148. package/server/services/agent-symphony.js +717 -717
  149. package/server/services/agent-tasks.js +1807 -1807
  150. package/server/services/api-key-engine.js +292 -0
  151. package/server/services/cluster.js +894 -894
  152. package/server/services/commander.js +738 -738
  153. package/server/services/edge-compute.js +440 -440
  154. package/server/services/email.js +204 -204
  155. package/server/services/hosted-runtime.js +205 -205
  156. package/server/services/lfd.js +635 -616
  157. package/server/services/local-ai.js +389 -389
  158. package/server/services/marketplace.js +270 -270
  159. package/server/services/metering.js +182 -182
  160. package/server/services/modules/affiliate-intelligence.js +93 -0
  161. package/server/services/modules/agent-firewall.js +90 -0
  162. package/server/services/modules/bounty.js +89 -0
  163. package/server/services/modules/collective-bargaining.js +92 -0
  164. package/server/services/modules/dark-pattern.js +66 -0
  165. package/server/services/modules/gov-intelligence.js +45 -0
  166. package/server/services/modules/neural.js +55 -0
  167. package/server/services/modules/notary.js +49 -0
  168. package/server/services/modules/price-time-machine.js +86 -0
  169. package/server/services/modules/protocol.js +104 -0
  170. package/server/services/negotiation.js +439 -439
  171. package/server/services/plugins.js +771 -771
  172. package/server/services/premium.js +1 -1
  173. package/server/services/price-intelligence.js +566 -565
  174. package/server/services/price-shield.js +1137 -1137
  175. package/server/services/reputation.js +465 -465
  176. package/server/services/search-engine.js +357 -357
  177. package/server/services/security.js +513 -513
  178. package/server/services/self-healing.js +843 -843
  179. package/server/services/sovereign-shield.js +542 -0
  180. package/server/services/stripe.js +192 -192
  181. package/server/services/swarm.js +788 -788
  182. package/server/services/universal-scraper.js +662 -661
  183. package/server/services/verification.js +481 -481
  184. package/server/services/vision.js +1163 -1163
  185. package/server/utils/cache.js +125 -125
  186. package/server/utils/migrate.js +81 -81
  187. package/server/utils/safe-fetch.js +228 -0
  188. package/server/utils/secureFields.js +50 -50
  189. package/server/ws.js +161 -161
  190. package/templates/artisan-marketplace.yaml +104 -104
  191. package/templates/book-price-scout.yaml +98 -98
  192. package/templates/electronics-price-tracker.yaml +108 -108
  193. package/templates/flight-deal-hunter.yaml +113 -113
  194. package/templates/freelancer-direct.yaml +116 -116
  195. package/templates/grocery-price-compare.yaml +93 -93
  196. package/templates/hotel-direct-booking.yaml +113 -113
  197. package/templates/local-services.yaml +98 -98
  198. package/templates/olive-oil-tunisia.yaml +88 -88
  199. package/templates/organic-farm-fresh.yaml +101 -101
  200. package/templates/restaurant-direct.yaml +97 -97
  201. package/server/services/fairness-engine.js +0 -409
  202. package/server/services/fairness.js +0 -420
@@ -1,724 +1,724 @@
1
- const express = require('express');
2
- const router = express.Router();
3
- const { authenticateToken } = require('../middleware/auth');
4
- const premium = require('../services/premium');
5
- const { findSiteById, findSitesByUser } = require('../models/db');
6
-
7
- function requireSiteOwnership(req, res, next) {
8
- const siteId = req.params.siteId || req.body.siteId;
9
- if (!siteId) return res.status(400).json({ error: 'siteId required' });
10
- const site = findSiteById.get(siteId);
11
- if (!site || site.user_id !== req.user.id) return res.status(403).json({ error: 'Access denied' });
12
- req.site = site;
13
- next();
14
- }
15
-
16
- // ─── Traffic Intelligence ────────────────────────────────────────────────
17
-
18
- router.get('/traffic/:siteId/profiles', authenticateToken, requireSiteOwnership, async (req, res) => {
19
- try {
20
- const { limit, offset, type } = req.query;
21
- const profiles = await premium.getAgentProfiles(req.params.siteId, {
22
- limit: limit ? parseInt(limit) : undefined,
23
- offset: offset ? parseInt(offset) : undefined,
24
- type
25
- });
26
- res.json({ profiles });
27
- } catch (err) {
28
- res.status(500).json({ error: 'Failed to fetch agent profiles' });
29
- }
30
- });
31
-
32
- router.get('/traffic/:siteId/stats', authenticateToken, requireSiteOwnership, async (req, res) => {
33
- try {
34
- const days = req.query.days ? parseInt(req.query.days) : 30;
35
- const stats = await premium.getTrafficStats(req.params.siteId, days);
36
- res.json({ stats });
37
- } catch (err) {
38
- res.status(500).json({ error: 'Failed to fetch traffic stats' });
39
- }
40
- });
41
-
42
- router.get('/traffic/:siteId/alerts', authenticateToken, requireSiteOwnership, async (req, res) => {
43
- try {
44
- const { limit, acknowledged } = req.query;
45
- const alerts = await premium.getAnomalyAlerts(req.params.siteId, {
46
- limit: limit ? parseInt(limit) : undefined,
47
- acknowledged: acknowledged !== undefined ? acknowledged === 'true' : undefined
48
- });
49
- res.json({ alerts });
50
- } catch (err) {
51
- res.status(500).json({ error: 'Failed to fetch anomaly alerts' });
52
- }
53
- });
54
-
55
- router.post('/traffic/:siteId/alerts/:alertId/acknowledge', authenticateToken, requireSiteOwnership, async (req, res) => {
56
- try {
57
- const ok = await premium.acknowledgeAlert(req.params.alertId, req.params.siteId);
58
- if (!ok) return res.status(404).json({ error: 'Alert not found' });
59
- res.json({ success: true });
60
- } catch (err) {
61
- res.status(500).json({ error: 'Failed to acknowledge alert' });
62
- }
63
- });
64
-
65
- router.post('/traffic/:siteId/check-anomalies', authenticateToken, requireSiteOwnership, async (req, res) => {
66
- try {
67
- const alerts = await premium.checkForAnomalies(req.params.siteId);
68
- res.json({ alerts });
69
- } catch (err) {
70
- res.status(500).json({ error: 'Failed to check for anomalies' });
71
- }
72
- });
73
-
74
- // ─── Exploit Shield ──────────────────────────────────────────────────────
75
-
76
- router.get('/security/:siteId/events', authenticateToken, requireSiteOwnership, async (req, res) => {
77
- try {
78
- const { limit, severity, since } = req.query;
79
- const events = await premium.getSecurityEvents(req.params.siteId, {
80
- limit: limit ? parseInt(limit) : undefined,
81
- severity,
82
- since
83
- });
84
- res.json({ events });
85
- } catch (err) {
86
- res.status(500).json({ error: 'Failed to fetch security events' });
87
- }
88
- });
89
-
90
- router.get('/security/:siteId/report', authenticateToken, requireSiteOwnership, async (req, res) => {
91
- try {
92
- const days = req.query.days ? parseInt(req.query.days) : 30;
93
- const report = await premium.getSecurityReport(req.params.siteId, days);
94
- res.json({ report });
95
- } catch (err) {
96
- res.status(500).json({ error: 'Failed to generate security report' });
97
- }
98
- });
99
-
100
- router.get('/security/:siteId/blocked', authenticateToken, requireSiteOwnership, async (req, res) => {
101
- try {
102
- const blocked = await premium.getBlockedAgents(req.params.siteId);
103
- res.json({ blocked });
104
- } catch (err) {
105
- res.status(500).json({ error: 'Failed to fetch blocked agents' });
106
- }
107
- });
108
-
109
- router.post('/security/:siteId/block', authenticateToken, requireSiteOwnership, async (req, res) => {
110
- try {
111
- const { agentSignature, reason, expiresAt } = req.body;
112
- if (!agentSignature) return res.status(400).json({ error: 'agentSignature is required' });
113
- const record = await premium.blockAgent(req.params.siteId, { agentSignature, reason, expiresAt });
114
- res.status(201).json({ blocked: record });
115
- } catch (err) {
116
- res.status(500).json({ error: 'Failed to block agent' });
117
- }
118
- });
119
-
120
- router.delete('/security/:siteId/block/:blockId', authenticateToken, requireSiteOwnership, async (req, res) => {
121
- try {
122
- const ok = await premium.unblockAgent(req.params.blockId, req.params.siteId);
123
- if (!ok) return res.status(404).json({ error: 'Block record not found' });
124
- res.json({ success: true });
125
- } catch (err) {
126
- res.status(500).json({ error: 'Failed to unblock agent' });
127
- }
128
- });
129
-
130
- // ─── Actions Library ─────────────────────────────────────────────────────
131
-
132
- router.get('/actions/packs', authenticateToken, async (req, res) => {
133
- try {
134
- const { platform, tier } = req.query;
135
- const packs = await premium.getActionPacks({
136
- platform,
137
- tierRequired: tier
138
- });
139
- res.json({ packs });
140
- } catch (err) {
141
- res.status(500).json({ error: 'Failed to fetch action packs' });
142
- }
143
- });
144
-
145
- router.get('/actions/packs/:packId', authenticateToken, async (req, res) => {
146
- try {
147
- const pack = await premium.getActionPack(req.params.packId);
148
- if (!pack) return res.status(404).json({ error: 'Pack not found' });
149
- res.json({ pack });
150
- } catch (err) {
151
- res.status(500).json({ error: 'Failed to fetch action pack' });
152
- }
153
- });
154
-
155
- router.get('/actions/packs/:packId/actions', authenticateToken, async (req, res) => {
156
- try {
157
- const actions = await premium.getPackActions(req.params.packId);
158
- res.json({ actions });
159
- } catch (err) {
160
- res.status(500).json({ error: 'Failed to fetch pack actions' });
161
- }
162
- });
163
-
164
- router.get('/actions/:siteId/installed', authenticateToken, requireSiteOwnership, async (req, res) => {
165
- try {
166
- const installed = await premium.getInstalledPacks(req.params.siteId);
167
- res.json({ installed });
168
- } catch (err) {
169
- res.status(500).json({ error: 'Failed to fetch installed packs' });
170
- }
171
- });
172
-
173
- router.post('/actions/:siteId/install', authenticateToken, requireSiteOwnership, async (req, res) => {
174
- try {
175
- const { packId, config } = req.body;
176
- if (!packId) return res.status(400).json({ error: 'packId is required' });
177
- const installation = await premium.installPack(req.params.siteId, packId, config);
178
- res.status(201).json({ installation });
179
- } catch (err) {
180
- res.status(500).json({ error: 'Failed to install pack' });
181
- }
182
- });
183
-
184
- router.delete('/actions/:siteId/install/:installId', authenticateToken, requireSiteOwnership, async (req, res) => {
185
- try {
186
- const ok = await premium.uninstallPack(req.params.installId, req.params.siteId);
187
- if (!ok) return res.status(404).json({ error: 'Installation not found' });
188
- res.json({ success: true });
189
- } catch (err) {
190
- res.status(500).json({ error: 'Failed to uninstall pack' });
191
- }
192
- });
193
-
194
- // ─── Custom Agents ───────────────────────────────────────────────────────
195
-
196
- router.get('/agents/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
197
- try {
198
- const agents = await premium.getAgents(req.user.id, req.params.siteId);
199
- res.json({ agents });
200
- } catch (err) {
201
- res.status(500).json({ error: 'Failed to fetch agents' });
202
- }
203
- });
204
-
205
- router.post('/agents/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
206
- try {
207
- const { name, description, steps, schedule } = req.body;
208
- if (!name || !steps) return res.status(400).json({ error: 'name and steps are required' });
209
- const agent = await premium.createAgent(req.user.id, req.params.siteId, { name, description, steps, schedule });
210
- res.status(201).json({ agent });
211
- } catch (err) {
212
- res.status(500).json({ error: 'Failed to create agent' });
213
- }
214
- });
215
-
216
- router.get('/agents/:siteId/:agentId', authenticateToken, requireSiteOwnership, async (req, res) => {
217
- try {
218
- const agent = await premium.getAgent(req.params.agentId, req.user.id);
219
- if (!agent) return res.status(404).json({ error: 'Agent not found' });
220
- res.json({ agent });
221
- } catch (err) {
222
- res.status(500).json({ error: 'Failed to fetch agent' });
223
- }
224
- });
225
-
226
- router.put('/agents/:siteId/:agentId', authenticateToken, requireSiteOwnership, async (req, res) => {
227
- try {
228
- const { name, description, steps, schedule } = req.body;
229
- const ok = await premium.updateAgent(req.params.agentId, req.user.id, { name, description, steps, schedule });
230
- if (!ok) return res.status(404).json({ error: 'Agent not found' });
231
- res.json({ success: true });
232
- } catch (err) {
233
- res.status(500).json({ error: 'Failed to update agent' });
234
- }
235
- });
236
-
237
- router.delete('/agents/:siteId/:agentId', authenticateToken, requireSiteOwnership, async (req, res) => {
238
- try {
239
- const ok = await premium.deleteAgent(req.params.agentId, req.user.id);
240
- if (!ok) return res.status(404).json({ error: 'Agent not found' });
241
- res.json({ success: true });
242
- } catch (err) {
243
- res.status(500).json({ error: 'Failed to delete agent' });
244
- }
245
- });
246
-
247
- router.post('/agents/:siteId/:agentId/run', authenticateToken, requireSiteOwnership, async (req, res) => {
248
- try {
249
- const result = await premium.runAgent(req.params.agentId, req.user.id);
250
- res.json({ result });
251
- } catch (err) {
252
- res.status(500).json({ error: 'Failed to run agent' });
253
- }
254
- });
255
-
256
- router.get('/agents/:siteId/:agentId/runs', authenticateToken, requireSiteOwnership, async (req, res) => {
257
- try {
258
- const { limit } = req.query;
259
- const runs = await premium.getAgentRuns(req.params.agentId, {
260
- limit: limit ? parseInt(limit) : undefined
261
- });
262
- res.json({ runs });
263
- } catch (err) {
264
- res.status(500).json({ error: 'Failed to fetch agent runs' });
265
- }
266
- });
267
-
268
- // ─── Webhooks & CRM ─────────────────────────────────────────────────────
269
-
270
- router.get('/webhooks/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
271
- try {
272
- const webhooks = await premium.getWebhooks(req.params.siteId);
273
- res.json({ webhooks });
274
- } catch (err) {
275
- res.status(500).json({ error: 'Failed to fetch webhooks' });
276
- }
277
- });
278
-
279
- router.post('/webhooks/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
280
- try {
281
- const { name, url, events, secret } = req.body;
282
- if (!name || !url || !events) return res.status(400).json({ error: 'name, url, and events are required' });
283
- const webhook = await premium.createWebhook(req.params.siteId, { name, url, events, secret });
284
- res.status(201).json({ webhook });
285
- } catch (err) {
286
- res.status(500).json({ error: 'Failed to create webhook' });
287
- }
288
- });
289
-
290
- router.put('/webhooks/:siteId/:webhookId', authenticateToken, requireSiteOwnership, async (req, res) => {
291
- try {
292
- const { name, url, events, secret, active } = req.body;
293
- const ok = await premium.updateWebhook(req.params.webhookId, req.params.siteId, { name, url, events, secret, active });
294
- if (!ok) return res.status(404).json({ error: 'Webhook not found' });
295
- res.json({ success: true });
296
- } catch (err) {
297
- res.status(500).json({ error: 'Failed to update webhook' });
298
- }
299
- });
300
-
301
- router.delete('/webhooks/:siteId/:webhookId', authenticateToken, requireSiteOwnership, async (req, res) => {
302
- try {
303
- const ok = await premium.deleteWebhook(req.params.webhookId, req.params.siteId);
304
- if (!ok) return res.status(404).json({ error: 'Webhook not found' });
305
- res.json({ success: true });
306
- } catch (err) {
307
- res.status(500).json({ error: 'Failed to delete webhook' });
308
- }
309
- });
310
-
311
- router.get('/webhooks/:siteId/:webhookId/logs', authenticateToken, requireSiteOwnership, async (req, res) => {
312
- try {
313
- const { limit } = req.query;
314
- const logs = await premium.getWebhookLogs(req.params.webhookId, {
315
- limit: limit ? parseInt(limit) : undefined
316
- });
317
- res.json({ logs });
318
- } catch (err) {
319
- res.status(500).json({ error: 'Failed to fetch webhook logs' });
320
- }
321
- });
322
-
323
- router.post('/webhooks/:siteId/test', authenticateToken, requireSiteOwnership, async (req, res) => {
324
- try {
325
- const { eventType, payload } = req.body;
326
- if (!eventType) return res.status(400).json({ error: 'eventType is required' });
327
- const results = await premium.triggerWebhooks(req.params.siteId, eventType, payload || {});
328
- res.json({ results });
329
- } catch (err) {
330
- res.status(500).json({ error: 'Failed to trigger webhooks' });
331
- }
332
- });
333
-
334
- router.get('/crm/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
335
- try {
336
- const integrations = await premium.getCrmIntegrations(req.params.siteId);
337
- res.json({ integrations });
338
- } catch (err) {
339
- res.status(500).json({ error: 'Failed to fetch CRM integrations' });
340
- }
341
- });
342
-
343
- router.post('/crm/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
344
- try {
345
- const { provider, config } = req.body;
346
- if (!provider || !config) return res.status(400).json({ error: 'provider and config are required' });
347
- const integration = await premium.addCrmIntegration(req.params.siteId, { provider, config });
348
- res.status(201).json({ integration });
349
- } catch (err) {
350
- res.status(500).json({ error: 'Failed to add CRM integration' });
351
- }
352
- });
353
-
354
- router.put('/crm/:siteId/:integrationId', authenticateToken, requireSiteOwnership, async (req, res) => {
355
- try {
356
- const { provider, config, active } = req.body;
357
- const ok = await premium.updateCrmIntegration(req.params.integrationId, req.params.siteId, { provider, config, active });
358
- if (!ok) return res.status(404).json({ error: 'Integration not found' });
359
- res.json({ success: true });
360
- } catch (err) {
361
- res.status(500).json({ error: 'Failed to update CRM integration' });
362
- }
363
- });
364
-
365
- router.delete('/crm/:siteId/:integrationId', authenticateToken, requireSiteOwnership, async (req, res) => {
366
- try {
367
- const ok = await premium.deleteCrmIntegration(req.params.integrationId, req.params.siteId);
368
- if (!ok) return res.status(404).json({ error: 'Integration not found' });
369
- res.json({ success: true });
370
- } catch (err) {
371
- res.status(500).json({ error: 'Failed to delete CRM integration' });
372
- }
373
- });
374
-
375
- // ─── Multi-Tenant ────────────────────────────────────────────────────────
376
-
377
- router.get('/team', authenticateToken, async (req, res) => {
378
- try {
379
- const subUsers = await premium.getSubUsers(req.user.id);
380
- res.json({ subUsers });
381
- } catch (err) {
382
- res.status(500).json({ error: 'Failed to fetch team members' });
383
- }
384
- });
385
-
386
- router.post('/team', authenticateToken, async (req, res) => {
387
- try {
388
- const { email, name, password, role, siteAccess, quotaActionsMonth } = req.body;
389
- if (!email || !name || !password) return res.status(400).json({ error: 'email, name, and password are required' });
390
- const subUser = await premium.inviteSubUser(req.user.id, { email, name, password, role, siteAccess, quotaActionsMonth });
391
- res.status(201).json({ subUser });
392
- } catch (err) {
393
- res.status(500).json({ error: 'Failed to invite team member' });
394
- }
395
- });
396
-
397
- router.put('/team/:subUserId', authenticateToken, async (req, res) => {
398
- try {
399
- const { name, role, siteAccess, quotaActionsMonth, active } = req.body;
400
- const ok = await premium.updateSubUser(req.params.subUserId, req.user.id, { name, role, siteAccess, quotaActionsMonth, active });
401
- if (!ok) return res.status(404).json({ error: 'Team member not found' });
402
- res.json({ success: true });
403
- } catch (err) {
404
- res.status(500).json({ error: 'Failed to update team member' });
405
- }
406
- });
407
-
408
- router.delete('/team/:subUserId', authenticateToken, async (req, res) => {
409
- try {
410
- const ok = await premium.deleteSubUser(req.params.subUserId, req.user.id);
411
- if (!ok) return res.status(404).json({ error: 'Team member not found' });
412
- res.json({ success: true });
413
- } catch (err) {
414
- res.status(500).json({ error: 'Failed to delete team member' });
415
- }
416
- });
417
-
418
- // ─── Support ─────────────────────────────────────────────────────────────
419
-
420
- router.get('/support/tickets', authenticateToken, async (req, res) => {
421
- try {
422
- const { status, limit } = req.query;
423
- const tickets = await premium.getTickets(req.user.id, {
424
- status,
425
- limit: limit ? parseInt(limit) : undefined
426
- });
427
- res.json({ tickets });
428
- } catch (err) {
429
- res.status(500).json({ error: 'Failed to fetch tickets' });
430
- }
431
- });
432
-
433
- router.post('/support/tickets', authenticateToken, async (req, res) => {
434
- try {
435
- const { subject, priority, category } = req.body;
436
- if (!subject) return res.status(400).json({ error: 'subject is required' });
437
- const ticket = await premium.createTicket(req.user.id, { subject, priority, category });
438
- res.status(201).json({ ticket });
439
- } catch (err) {
440
- res.status(500).json({ error: 'Failed to create ticket' });
441
- }
442
- });
443
-
444
- router.get('/support/stats', authenticateToken, async (req, res) => {
445
- try {
446
- const stats = await premium.getTicketStats(req.user.id);
447
- res.json({ stats });
448
- } catch (err) {
449
- res.status(500).json({ error: 'Failed to fetch ticket stats' });
450
- }
451
- });
452
-
453
- router.get('/support/tickets/:ticketId', authenticateToken, async (req, res) => {
454
- try {
455
- const ticket = await premium.getTicket(req.params.ticketId, req.user.id);
456
- if (!ticket) return res.status(404).json({ error: 'Ticket not found' });
457
- const messages = await premium.getTicketMessages(req.params.ticketId);
458
- res.json({ ticket, messages });
459
- } catch (err) {
460
- res.status(500).json({ error: 'Failed to fetch ticket' });
461
- }
462
- });
463
-
464
- router.put('/support/tickets/:ticketId/status', authenticateToken, async (req, res) => {
465
- try {
466
- const { status } = req.body;
467
- if (!status) return res.status(400).json({ error: 'status is required' });
468
- const ok = await premium.updateTicketStatus(req.params.ticketId, req.user.id, status);
469
- if (!ok) return res.status(404).json({ error: 'Ticket not found' });
470
- res.json({ success: true });
471
- } catch (err) {
472
- res.status(500).json({ error: 'Failed to update ticket status' });
473
- }
474
- });
475
-
476
- router.post('/support/tickets/:ticketId/messages', authenticateToken, async (req, res) => {
477
- try {
478
- const { message } = req.body;
479
- if (!message) return res.status(400).json({ error: 'message is required' });
480
- const msg = await premium.addTicketMessage(req.params.ticketId, {
481
- senderType: 'user',
482
- senderId: req.user.id,
483
- message
484
- });
485
-
486
- const botReply = await premium.generateBotResponse(message);
487
- let botMsg = null;
488
- if (botReply) {
489
- botMsg = await premium.addTicketMessage(req.params.ticketId, {
490
- senderType: 'bot',
491
- senderId: 'system',
492
- message: botReply
493
- });
494
- }
495
-
496
- res.status(201).json({ message: msg, botReply: botMsg });
497
- } catch (err) {
498
- res.status(500).json({ error: 'Failed to add message' });
499
- }
500
- });
501
-
502
- // ─── Custom Script ───────────────────────────────────────────────────────
503
-
504
- router.get('/script/plugins', authenticateToken, async (req, res) => {
505
- try {
506
- const plugins = await premium.getAvailablePlugins();
507
- res.json({ plugins });
508
- } catch (err) {
509
- res.status(500).json({ error: 'Failed to fetch plugins' });
510
- }
511
- });
512
-
513
- router.get('/script/:siteId/config', authenticateToken, requireSiteOwnership, async (req, res) => {
514
- try {
515
- const config = await premium.getScriptConfig(req.params.siteId);
516
- res.json({ config });
517
- } catch (err) {
518
- res.status(500).json({ error: 'Failed to fetch script config' });
519
- }
520
- });
521
-
522
- router.put('/script/:siteId/config', authenticateToken, requireSiteOwnership, async (req, res) => {
523
- try {
524
- const { plugins, minified, ampCompatible, autoPatch, customCss, customJs } = req.body;
525
- const config = await premium.updateScriptConfig(req.params.siteId, {
526
- plugins, minified, ampCompatible, autoPatch, customCss, customJs
527
- });
528
- res.json({ config });
529
- } catch (err) {
530
- res.status(500).json({ error: 'Failed to update script config' });
531
- }
532
- });
533
-
534
- router.post('/script/:siteId/build', authenticateToken, requireSiteOwnership, async (req, res) => {
535
- try {
536
- const result = await premium.buildScript(req.params.siteId);
537
- res.json(result);
538
- } catch (err) {
539
- res.status(500).json({ error: 'Failed to build script' });
540
- }
541
- });
542
-
543
- // ─── Stealth ─────────────────────────────────────────────────────────────
544
-
545
- router.get('/stealth/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
546
- try {
547
- const profile = await premium.getStealthProfile(req.params.siteId);
548
- res.json({ profile });
549
- } catch (err) {
550
- res.status(500).json({ error: 'Failed to fetch stealth profile' });
551
- }
552
- });
553
-
554
- router.put('/stealth/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
555
- try {
556
- const profile = await premium.upsertStealthProfile(req.params.siteId, req.body);
557
- res.json({ profile });
558
- } catch (err) {
559
- res.status(500).json({ error: 'Failed to update stealth profile' });
560
- }
561
- });
562
-
563
- router.get('/stealth/:siteId/script', authenticateToken, requireSiteOwnership, async (req, res) => {
564
- try {
565
- const script = await premium.generateStealthScript(req.params.siteId);
566
- res.json({ script });
567
- } catch (err) {
568
- res.status(500).json({ error: 'Failed to generate stealth script' });
569
- }
570
- });
571
-
572
- // ─── CDN ─────────────────────────────────────────────────────────────────
573
-
574
- router.get('/cdn/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
575
- try {
576
- const config = await premium.getCdnConfig(req.params.siteId);
577
- res.json({ config });
578
- } catch (err) {
579
- res.status(500).json({ error: 'Failed to fetch CDN config' });
580
- }
581
- });
582
-
583
- router.put('/cdn/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
584
- try {
585
- const config = await premium.upsertCdnConfig(req.params.siteId, req.body);
586
- res.json({ config });
587
- } catch (err) {
588
- res.status(500).json({ error: 'Failed to update CDN config' });
589
- }
590
- });
591
-
592
- router.get('/cdn/:siteId/stats', authenticateToken, requireSiteOwnership, async (req, res) => {
593
- try {
594
- const days = req.query.days ? parseInt(req.query.days) : 30;
595
- const cdnConfig = await premium.getCdnConfig(req.params.siteId);
596
- if (!cdnConfig) return res.status(404).json({ error: 'CDN not configured' });
597
- const stats = await premium.getCdnStats(cdnConfig.id, days);
598
- res.json({ stats });
599
- } catch (err) {
600
- res.status(500).json({ error: 'Failed to fetch CDN stats' });
601
- }
602
- });
603
-
604
- // ─── Audit ───────────────────────────────────────────────────────────────
605
-
606
- router.get('/audit/:siteId/logs', authenticateToken, requireSiteOwnership, async (req, res) => {
607
- try {
608
- const { limit, offset, action, since, until } = req.query;
609
- const result = await premium.getAuditLogs(req.params.siteId, {
610
- limit: limit ? parseInt(limit) : undefined,
611
- offset: offset ? parseInt(offset) : undefined,
612
- action,
613
- since,
614
- until
615
- });
616
- res.json(result);
617
- } catch (err) {
618
- res.status(500).json({ error: 'Failed to fetch audit logs' });
619
- }
620
- });
621
-
622
- router.get('/audit/:siteId/compliance', authenticateToken, requireSiteOwnership, async (req, res) => {
623
- try {
624
- const settings = await premium.getComplianceSettings(req.params.siteId);
625
- res.json({ settings });
626
- } catch (err) {
627
- res.status(500).json({ error: 'Failed to fetch compliance settings' });
628
- }
629
- });
630
-
631
- router.put('/audit/:siteId/compliance', authenticateToken, requireSiteOwnership, async (req, res) => {
632
- try {
633
- const settings = await premium.upsertComplianceSettings(req.params.siteId, req.body);
634
- res.json({ settings });
635
- } catch (err) {
636
- res.status(500).json({ error: 'Failed to update compliance settings' });
637
- }
638
- });
639
-
640
- router.get('/audit/:siteId/export', authenticateToken, requireSiteOwnership, async (req, res) => {
641
- try {
642
- const { format, since, until } = req.query;
643
- const result = await premium.exportAuditLogs(req.params.siteId, { format, since, until });
644
- res.setHeader('Content-Disposition', `attachment; filename="${result.filename}"`);
645
- res.setHeader('Content-Type', result.contentType);
646
- res.send(result.data);
647
- } catch (err) {
648
- res.status(500).json({ error: 'Failed to export audit logs' });
649
- }
650
- });
651
-
652
- router.post('/audit/:siteId/purge', authenticateToken, requireSiteOwnership, async (req, res) => {
653
- try {
654
- const result = await premium.purgeOldLogs(req.params.siteId);
655
- res.json(result);
656
- } catch (err) {
657
- res.status(500).json({ error: 'Failed to purge old logs' });
658
- }
659
- });
660
-
661
- // ─── Sandbox ─────────────────────────────────────────────────────────────
662
-
663
- router.get('/sandbox/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
664
- try {
665
- const sandboxes = await premium.getSandboxes(req.params.siteId);
666
- res.json({ sandboxes });
667
- } catch (err) {
668
- res.status(500).json({ error: 'Failed to fetch sandboxes' });
669
- }
670
- });
671
-
672
- router.post('/sandbox/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
673
- try {
674
- const { name } = req.body;
675
- if (!name) return res.status(400).json({ error: 'name is required' });
676
- const sandbox = await premium.createSandbox(req.params.siteId, { name });
677
- res.status(201).json({ sandbox });
678
- } catch (err) {
679
- res.status(500).json({ error: 'Failed to create sandbox' });
680
- }
681
- });
682
-
683
- router.delete('/sandbox/:siteId/:sandboxId', authenticateToken, requireSiteOwnership, async (req, res) => {
684
- try {
685
- const ok = await premium.deleteSandbox(req.params.sandboxId, req.params.siteId);
686
- if (!ok) return res.status(404).json({ error: 'Sandbox not found' });
687
- res.json({ success: true });
688
- } catch (err) {
689
- res.status(500).json({ error: 'Failed to delete sandbox' });
690
- }
691
- });
692
-
693
- router.post('/sandbox/:siteId/:sandboxId/simulate', authenticateToken, requireSiteOwnership, async (req, res) => {
694
- try {
695
- const { agentCount, duration, actionsPerAgent } = req.body;
696
- if (!agentCount || !duration) return res.status(400).json({ error: 'agentCount and duration are required' });
697
- const result = await premium.simulateTraffic(req.params.sandboxId, { agentCount, duration, actionsPerAgent });
698
- res.json({ result });
699
- } catch (err) {
700
- res.status(500).json({ error: 'Failed to simulate traffic' });
701
- }
702
- });
703
-
704
- router.post('/sandbox/:siteId/:sandboxId/benchmark', authenticateToken, requireSiteOwnership, async (req, res) => {
705
- try {
706
- const { benchmarkType } = req.body;
707
- if (!benchmarkType) return res.status(400).json({ error: 'benchmarkType is required' });
708
- const result = await premium.runBenchmark(req.params.sandboxId, { benchmarkType });
709
- res.json({ result });
710
- } catch (err) {
711
- res.status(500).json({ error: 'Failed to run benchmark' });
712
- }
713
- });
714
-
715
- router.get('/sandbox/:siteId/:sandboxId/benchmarks', authenticateToken, requireSiteOwnership, async (req, res) => {
716
- try {
717
- const benchmarks = await premium.getBenchmarks(req.params.sandboxId);
718
- res.json({ benchmarks });
719
- } catch (err) {
720
- res.status(500).json({ error: 'Failed to fetch benchmarks' });
721
- }
722
- });
723
-
724
- module.exports = router;
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const { authenticateToken } = require('../middleware/auth');
4
+ const premium = require('../services/premium');
5
+ const { findSiteById, findSitesByUser } = require('../models/db');
6
+
7
+ function requireSiteOwnership(req, res, next) {
8
+ const siteId = req.params.siteId || req.body.siteId;
9
+ if (!siteId) return res.status(400).json({ error: 'siteId required' });
10
+ const site = findSiteById.get(siteId);
11
+ if (!site || site.user_id !== req.user.id) return res.status(403).json({ error: 'Access denied' });
12
+ req.site = site;
13
+ next();
14
+ }
15
+
16
+ // ─── Traffic Intelligence ────────────────────────────────────────────────
17
+
18
+ router.get('/traffic/:siteId/profiles', authenticateToken, requireSiteOwnership, async (req, res) => {
19
+ try {
20
+ const { limit, offset, type } = req.query;
21
+ const profiles = await premium.getAgentProfiles(req.params.siteId, {
22
+ limit: limit ? parseInt(limit) : undefined,
23
+ offset: offset ? parseInt(offset) : undefined,
24
+ type
25
+ });
26
+ res.json({ profiles });
27
+ } catch (err) {
28
+ res.status(500).json({ error: 'Failed to fetch agent profiles' });
29
+ }
30
+ });
31
+
32
+ router.get('/traffic/:siteId/stats', authenticateToken, requireSiteOwnership, async (req, res) => {
33
+ try {
34
+ const days = req.query.days ? parseInt(req.query.days) : 30;
35
+ const stats = await premium.getTrafficStats(req.params.siteId, days);
36
+ res.json({ stats });
37
+ } catch (err) {
38
+ res.status(500).json({ error: 'Failed to fetch traffic stats' });
39
+ }
40
+ });
41
+
42
+ router.get('/traffic/:siteId/alerts', authenticateToken, requireSiteOwnership, async (req, res) => {
43
+ try {
44
+ const { limit, acknowledged } = req.query;
45
+ const alerts = await premium.getAnomalyAlerts(req.params.siteId, {
46
+ limit: limit ? parseInt(limit) : undefined,
47
+ acknowledged: acknowledged !== undefined ? acknowledged === 'true' : undefined
48
+ });
49
+ res.json({ alerts });
50
+ } catch (err) {
51
+ res.status(500).json({ error: 'Failed to fetch anomaly alerts' });
52
+ }
53
+ });
54
+
55
+ router.post('/traffic/:siteId/alerts/:alertId/acknowledge', authenticateToken, requireSiteOwnership, async (req, res) => {
56
+ try {
57
+ const ok = await premium.acknowledgeAlert(req.params.alertId, req.params.siteId);
58
+ if (!ok) return res.status(404).json({ error: 'Alert not found' });
59
+ res.json({ success: true });
60
+ } catch (err) {
61
+ res.status(500).json({ error: 'Failed to acknowledge alert' });
62
+ }
63
+ });
64
+
65
+ router.post('/traffic/:siteId/check-anomalies', authenticateToken, requireSiteOwnership, async (req, res) => {
66
+ try {
67
+ const alerts = await premium.checkForAnomalies(req.params.siteId);
68
+ res.json({ alerts });
69
+ } catch (err) {
70
+ res.status(500).json({ error: 'Failed to check for anomalies' });
71
+ }
72
+ });
73
+
74
+ // ─── Exploit Shield ──────────────────────────────────────────────────────
75
+
76
+ router.get('/security/:siteId/events', authenticateToken, requireSiteOwnership, async (req, res) => {
77
+ try {
78
+ const { limit, severity, since } = req.query;
79
+ const events = await premium.getSecurityEvents(req.params.siteId, {
80
+ limit: limit ? parseInt(limit) : undefined,
81
+ severity,
82
+ since
83
+ });
84
+ res.json({ events });
85
+ } catch (err) {
86
+ res.status(500).json({ error: 'Failed to fetch security events' });
87
+ }
88
+ });
89
+
90
+ router.get('/security/:siteId/report', authenticateToken, requireSiteOwnership, async (req, res) => {
91
+ try {
92
+ const days = req.query.days ? parseInt(req.query.days) : 30;
93
+ const report = await premium.getSecurityReport(req.params.siteId, days);
94
+ res.json({ report });
95
+ } catch (err) {
96
+ res.status(500).json({ error: 'Failed to generate security report' });
97
+ }
98
+ });
99
+
100
+ router.get('/security/:siteId/blocked', authenticateToken, requireSiteOwnership, async (req, res) => {
101
+ try {
102
+ const blocked = await premium.getBlockedAgents(req.params.siteId);
103
+ res.json({ blocked });
104
+ } catch (err) {
105
+ res.status(500).json({ error: 'Failed to fetch blocked agents' });
106
+ }
107
+ });
108
+
109
+ router.post('/security/:siteId/block', authenticateToken, requireSiteOwnership, async (req, res) => {
110
+ try {
111
+ const { agentSignature, reason, expiresAt } = req.body;
112
+ if (!agentSignature) return res.status(400).json({ error: 'agentSignature is required' });
113
+ const record = await premium.blockAgent(req.params.siteId, { agentSignature, reason, expiresAt });
114
+ res.status(201).json({ blocked: record });
115
+ } catch (err) {
116
+ res.status(500).json({ error: 'Failed to block agent' });
117
+ }
118
+ });
119
+
120
+ router.delete('/security/:siteId/block/:blockId', authenticateToken, requireSiteOwnership, async (req, res) => {
121
+ try {
122
+ const ok = await premium.unblockAgent(req.params.blockId, req.params.siteId);
123
+ if (!ok) return res.status(404).json({ error: 'Block record not found' });
124
+ res.json({ success: true });
125
+ } catch (err) {
126
+ res.status(500).json({ error: 'Failed to unblock agent' });
127
+ }
128
+ });
129
+
130
+ // ─── Actions Library ─────────────────────────────────────────────────────
131
+
132
+ router.get('/actions/packs', authenticateToken, async (req, res) => {
133
+ try {
134
+ const { platform, tier } = req.query;
135
+ const packs = await premium.getActionPacks({
136
+ platform,
137
+ tierRequired: tier
138
+ });
139
+ res.json({ packs });
140
+ } catch (err) {
141
+ res.status(500).json({ error: 'Failed to fetch action packs' });
142
+ }
143
+ });
144
+
145
+ router.get('/actions/packs/:packId', authenticateToken, async (req, res) => {
146
+ try {
147
+ const pack = await premium.getActionPack(req.params.packId);
148
+ if (!pack) return res.status(404).json({ error: 'Pack not found' });
149
+ res.json({ pack });
150
+ } catch (err) {
151
+ res.status(500).json({ error: 'Failed to fetch action pack' });
152
+ }
153
+ });
154
+
155
+ router.get('/actions/packs/:packId/actions', authenticateToken, async (req, res) => {
156
+ try {
157
+ const actions = await premium.getPackActions(req.params.packId);
158
+ res.json({ actions });
159
+ } catch (err) {
160
+ res.status(500).json({ error: 'Failed to fetch pack actions' });
161
+ }
162
+ });
163
+
164
+ router.get('/actions/:siteId/installed', authenticateToken, requireSiteOwnership, async (req, res) => {
165
+ try {
166
+ const installed = await premium.getInstalledPacks(req.params.siteId);
167
+ res.json({ installed });
168
+ } catch (err) {
169
+ res.status(500).json({ error: 'Failed to fetch installed packs' });
170
+ }
171
+ });
172
+
173
+ router.post('/actions/:siteId/install', authenticateToken, requireSiteOwnership, async (req, res) => {
174
+ try {
175
+ const { packId, config } = req.body;
176
+ if (!packId) return res.status(400).json({ error: 'packId is required' });
177
+ const installation = await premium.installPack(req.params.siteId, packId, config);
178
+ res.status(201).json({ installation });
179
+ } catch (err) {
180
+ res.status(500).json({ error: 'Failed to install pack' });
181
+ }
182
+ });
183
+
184
+ router.delete('/actions/:siteId/install/:installId', authenticateToken, requireSiteOwnership, async (req, res) => {
185
+ try {
186
+ const ok = await premium.uninstallPack(req.params.installId, req.params.siteId);
187
+ if (!ok) return res.status(404).json({ error: 'Installation not found' });
188
+ res.json({ success: true });
189
+ } catch (err) {
190
+ res.status(500).json({ error: 'Failed to uninstall pack' });
191
+ }
192
+ });
193
+
194
+ // ─── Custom Agents ───────────────────────────────────────────────────────
195
+
196
+ router.get('/agents/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
197
+ try {
198
+ const agents = await premium.getAgents(req.user.id, req.params.siteId);
199
+ res.json({ agents });
200
+ } catch (err) {
201
+ res.status(500).json({ error: 'Failed to fetch agents' });
202
+ }
203
+ });
204
+
205
+ router.post('/agents/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
206
+ try {
207
+ const { name, description, steps, schedule } = req.body;
208
+ if (!name || !steps) return res.status(400).json({ error: 'name and steps are required' });
209
+ const agent = await premium.createAgent(req.user.id, req.params.siteId, { name, description, steps, schedule });
210
+ res.status(201).json({ agent });
211
+ } catch (err) {
212
+ res.status(500).json({ error: 'Failed to create agent' });
213
+ }
214
+ });
215
+
216
+ router.get('/agents/:siteId/:agentId', authenticateToken, requireSiteOwnership, async (req, res) => {
217
+ try {
218
+ const agent = await premium.getAgent(req.params.agentId, req.user.id);
219
+ if (!agent) return res.status(404).json({ error: 'Agent not found' });
220
+ res.json({ agent });
221
+ } catch (err) {
222
+ res.status(500).json({ error: 'Failed to fetch agent' });
223
+ }
224
+ });
225
+
226
+ router.put('/agents/:siteId/:agentId', authenticateToken, requireSiteOwnership, async (req, res) => {
227
+ try {
228
+ const { name, description, steps, schedule } = req.body;
229
+ const ok = await premium.updateAgent(req.params.agentId, req.user.id, { name, description, steps, schedule });
230
+ if (!ok) return res.status(404).json({ error: 'Agent not found' });
231
+ res.json({ success: true });
232
+ } catch (err) {
233
+ res.status(500).json({ error: 'Failed to update agent' });
234
+ }
235
+ });
236
+
237
+ router.delete('/agents/:siteId/:agentId', authenticateToken, requireSiteOwnership, async (req, res) => {
238
+ try {
239
+ const ok = await premium.deleteAgent(req.params.agentId, req.user.id);
240
+ if (!ok) return res.status(404).json({ error: 'Agent not found' });
241
+ res.json({ success: true });
242
+ } catch (err) {
243
+ res.status(500).json({ error: 'Failed to delete agent' });
244
+ }
245
+ });
246
+
247
+ router.post('/agents/:siteId/:agentId/run', authenticateToken, requireSiteOwnership, async (req, res) => {
248
+ try {
249
+ const result = await premium.runAgent(req.params.agentId, req.user.id);
250
+ res.json({ result });
251
+ } catch (err) {
252
+ res.status(500).json({ error: 'Failed to run agent' });
253
+ }
254
+ });
255
+
256
+ router.get('/agents/:siteId/:agentId/runs', authenticateToken, requireSiteOwnership, async (req, res) => {
257
+ try {
258
+ const { limit } = req.query;
259
+ const runs = await premium.getAgentRuns(req.params.agentId, {
260
+ limit: limit ? parseInt(limit) : undefined
261
+ });
262
+ res.json({ runs });
263
+ } catch (err) {
264
+ res.status(500).json({ error: 'Failed to fetch agent runs' });
265
+ }
266
+ });
267
+
268
+ // ─── Webhooks & CRM ─────────────────────────────────────────────────────
269
+
270
+ router.get('/webhooks/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
271
+ try {
272
+ const webhooks = await premium.getWebhooks(req.params.siteId);
273
+ res.json({ webhooks });
274
+ } catch (err) {
275
+ res.status(500).json({ error: 'Failed to fetch webhooks' });
276
+ }
277
+ });
278
+
279
+ router.post('/webhooks/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
280
+ try {
281
+ const { name, url, events, secret } = req.body;
282
+ if (!name || !url || !events) return res.status(400).json({ error: 'name, url, and events are required' });
283
+ const webhook = await premium.createWebhook(req.params.siteId, { name, url, events, secret });
284
+ res.status(201).json({ webhook });
285
+ } catch (err) {
286
+ res.status(500).json({ error: 'Failed to create webhook' });
287
+ }
288
+ });
289
+
290
+ router.put('/webhooks/:siteId/:webhookId', authenticateToken, requireSiteOwnership, async (req, res) => {
291
+ try {
292
+ const { name, url, events, secret, active } = req.body;
293
+ const ok = await premium.updateWebhook(req.params.webhookId, req.params.siteId, { name, url, events, secret, active });
294
+ if (!ok) return res.status(404).json({ error: 'Webhook not found' });
295
+ res.json({ success: true });
296
+ } catch (err) {
297
+ res.status(500).json({ error: 'Failed to update webhook' });
298
+ }
299
+ });
300
+
301
+ router.delete('/webhooks/:siteId/:webhookId', authenticateToken, requireSiteOwnership, async (req, res) => {
302
+ try {
303
+ const ok = await premium.deleteWebhook(req.params.webhookId, req.params.siteId);
304
+ if (!ok) return res.status(404).json({ error: 'Webhook not found' });
305
+ res.json({ success: true });
306
+ } catch (err) {
307
+ res.status(500).json({ error: 'Failed to delete webhook' });
308
+ }
309
+ });
310
+
311
+ router.get('/webhooks/:siteId/:webhookId/logs', authenticateToken, requireSiteOwnership, async (req, res) => {
312
+ try {
313
+ const { limit } = req.query;
314
+ const logs = await premium.getWebhookLogs(req.params.webhookId, {
315
+ limit: limit ? parseInt(limit) : undefined
316
+ });
317
+ res.json({ logs });
318
+ } catch (err) {
319
+ res.status(500).json({ error: 'Failed to fetch webhook logs' });
320
+ }
321
+ });
322
+
323
+ router.post('/webhooks/:siteId/test', authenticateToken, requireSiteOwnership, async (req, res) => {
324
+ try {
325
+ const { eventType, payload } = req.body;
326
+ if (!eventType) return res.status(400).json({ error: 'eventType is required' });
327
+ const results = await premium.triggerWebhooks(req.params.siteId, eventType, payload || {});
328
+ res.json({ results });
329
+ } catch (err) {
330
+ res.status(500).json({ error: 'Failed to trigger webhooks' });
331
+ }
332
+ });
333
+
334
+ router.get('/crm/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
335
+ try {
336
+ const integrations = await premium.getCrmIntegrations(req.params.siteId);
337
+ res.json({ integrations });
338
+ } catch (err) {
339
+ res.status(500).json({ error: 'Failed to fetch CRM integrations' });
340
+ }
341
+ });
342
+
343
+ router.post('/crm/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
344
+ try {
345
+ const { provider, config } = req.body;
346
+ if (!provider || !config) return res.status(400).json({ error: 'provider and config are required' });
347
+ const integration = await premium.addCrmIntegration(req.params.siteId, { provider, config });
348
+ res.status(201).json({ integration });
349
+ } catch (err) {
350
+ res.status(500).json({ error: 'Failed to add CRM integration' });
351
+ }
352
+ });
353
+
354
+ router.put('/crm/:siteId/:integrationId', authenticateToken, requireSiteOwnership, async (req, res) => {
355
+ try {
356
+ const { provider, config, active } = req.body;
357
+ const ok = await premium.updateCrmIntegration(req.params.integrationId, req.params.siteId, { provider, config, active });
358
+ if (!ok) return res.status(404).json({ error: 'Integration not found' });
359
+ res.json({ success: true });
360
+ } catch (err) {
361
+ res.status(500).json({ error: 'Failed to update CRM integration' });
362
+ }
363
+ });
364
+
365
+ router.delete('/crm/:siteId/:integrationId', authenticateToken, requireSiteOwnership, async (req, res) => {
366
+ try {
367
+ const ok = await premium.deleteCrmIntegration(req.params.integrationId, req.params.siteId);
368
+ if (!ok) return res.status(404).json({ error: 'Integration not found' });
369
+ res.json({ success: true });
370
+ } catch (err) {
371
+ res.status(500).json({ error: 'Failed to delete CRM integration' });
372
+ }
373
+ });
374
+
375
+ // ─── Multi-Tenant ────────────────────────────────────────────────────────
376
+
377
+ router.get('/team', authenticateToken, async (req, res) => {
378
+ try {
379
+ const subUsers = await premium.getSubUsers(req.user.id);
380
+ res.json({ subUsers });
381
+ } catch (err) {
382
+ res.status(500).json({ error: 'Failed to fetch team members' });
383
+ }
384
+ });
385
+
386
+ router.post('/team', authenticateToken, async (req, res) => {
387
+ try {
388
+ const { email, name, password, role, siteAccess, quotaActionsMonth } = req.body;
389
+ if (!email || !name || !password) return res.status(400).json({ error: 'email, name, and password are required' });
390
+ const subUser = await premium.inviteSubUser(req.user.id, { email, name, password, role, siteAccess, quotaActionsMonth });
391
+ res.status(201).json({ subUser });
392
+ } catch (err) {
393
+ res.status(500).json({ error: 'Failed to invite team member' });
394
+ }
395
+ });
396
+
397
+ router.put('/team/:subUserId', authenticateToken, async (req, res) => {
398
+ try {
399
+ const { name, role, siteAccess, quotaActionsMonth, active } = req.body;
400
+ const ok = await premium.updateSubUser(req.params.subUserId, req.user.id, { name, role, siteAccess, quotaActionsMonth, active });
401
+ if (!ok) return res.status(404).json({ error: 'Team member not found' });
402
+ res.json({ success: true });
403
+ } catch (err) {
404
+ res.status(500).json({ error: 'Failed to update team member' });
405
+ }
406
+ });
407
+
408
+ router.delete('/team/:subUserId', authenticateToken, async (req, res) => {
409
+ try {
410
+ const ok = await premium.deleteSubUser(req.params.subUserId, req.user.id);
411
+ if (!ok) return res.status(404).json({ error: 'Team member not found' });
412
+ res.json({ success: true });
413
+ } catch (err) {
414
+ res.status(500).json({ error: 'Failed to delete team member' });
415
+ }
416
+ });
417
+
418
+ // ─── Support ─────────────────────────────────────────────────────────────
419
+
420
+ router.get('/support/tickets', authenticateToken, async (req, res) => {
421
+ try {
422
+ const { status, limit } = req.query;
423
+ const tickets = await premium.getTickets(req.user.id, {
424
+ status,
425
+ limit: limit ? parseInt(limit) : undefined
426
+ });
427
+ res.json({ tickets });
428
+ } catch (err) {
429
+ res.status(500).json({ error: 'Failed to fetch tickets' });
430
+ }
431
+ });
432
+
433
+ router.post('/support/tickets', authenticateToken, async (req, res) => {
434
+ try {
435
+ const { subject, priority, category } = req.body;
436
+ if (!subject) return res.status(400).json({ error: 'subject is required' });
437
+ const ticket = await premium.createTicket(req.user.id, { subject, priority, category });
438
+ res.status(201).json({ ticket });
439
+ } catch (err) {
440
+ res.status(500).json({ error: 'Failed to create ticket' });
441
+ }
442
+ });
443
+
444
+ router.get('/support/stats', authenticateToken, async (req, res) => {
445
+ try {
446
+ const stats = await premium.getTicketStats(req.user.id);
447
+ res.json({ stats });
448
+ } catch (err) {
449
+ res.status(500).json({ error: 'Failed to fetch ticket stats' });
450
+ }
451
+ });
452
+
453
+ router.get('/support/tickets/:ticketId', authenticateToken, async (req, res) => {
454
+ try {
455
+ const ticket = await premium.getTicket(req.params.ticketId, req.user.id);
456
+ if (!ticket) return res.status(404).json({ error: 'Ticket not found' });
457
+ const messages = await premium.getTicketMessages(req.params.ticketId);
458
+ res.json({ ticket, messages });
459
+ } catch (err) {
460
+ res.status(500).json({ error: 'Failed to fetch ticket' });
461
+ }
462
+ });
463
+
464
+ router.put('/support/tickets/:ticketId/status', authenticateToken, async (req, res) => {
465
+ try {
466
+ const { status } = req.body;
467
+ if (!status) return res.status(400).json({ error: 'status is required' });
468
+ const ok = await premium.updateTicketStatus(req.params.ticketId, req.user.id, status);
469
+ if (!ok) return res.status(404).json({ error: 'Ticket not found' });
470
+ res.json({ success: true });
471
+ } catch (err) {
472
+ res.status(500).json({ error: 'Failed to update ticket status' });
473
+ }
474
+ });
475
+
476
+ router.post('/support/tickets/:ticketId/messages', authenticateToken, async (req, res) => {
477
+ try {
478
+ const { message } = req.body;
479
+ if (!message) return res.status(400).json({ error: 'message is required' });
480
+ const msg = await premium.addTicketMessage(req.params.ticketId, {
481
+ senderType: 'user',
482
+ senderId: req.user.id,
483
+ message
484
+ });
485
+
486
+ const botReply = await premium.generateBotResponse(message);
487
+ let botMsg = null;
488
+ if (botReply) {
489
+ botMsg = await premium.addTicketMessage(req.params.ticketId, {
490
+ senderType: 'bot',
491
+ senderId: 'system',
492
+ message: botReply
493
+ });
494
+ }
495
+
496
+ res.status(201).json({ message: msg, botReply: botMsg });
497
+ } catch (err) {
498
+ res.status(500).json({ error: 'Failed to add message' });
499
+ }
500
+ });
501
+
502
+ // ─── Custom Script ───────────────────────────────────────────────────────
503
+
504
+ router.get('/script/plugins', authenticateToken, async (req, res) => {
505
+ try {
506
+ const plugins = await premium.getAvailablePlugins();
507
+ res.json({ plugins });
508
+ } catch (err) {
509
+ res.status(500).json({ error: 'Failed to fetch plugins' });
510
+ }
511
+ });
512
+
513
+ router.get('/script/:siteId/config', authenticateToken, requireSiteOwnership, async (req, res) => {
514
+ try {
515
+ const config = await premium.getScriptConfig(req.params.siteId);
516
+ res.json({ config });
517
+ } catch (err) {
518
+ res.status(500).json({ error: 'Failed to fetch script config' });
519
+ }
520
+ });
521
+
522
+ router.put('/script/:siteId/config', authenticateToken, requireSiteOwnership, async (req, res) => {
523
+ try {
524
+ const { plugins, minified, ampCompatible, autoPatch, customCss, customJs } = req.body;
525
+ const config = await premium.updateScriptConfig(req.params.siteId, {
526
+ plugins, minified, ampCompatible, autoPatch, customCss, customJs
527
+ });
528
+ res.json({ config });
529
+ } catch (err) {
530
+ res.status(500).json({ error: 'Failed to update script config' });
531
+ }
532
+ });
533
+
534
+ router.post('/script/:siteId/build', authenticateToken, requireSiteOwnership, async (req, res) => {
535
+ try {
536
+ const result = await premium.buildScript(req.params.siteId);
537
+ res.json(result);
538
+ } catch (err) {
539
+ res.status(500).json({ error: 'Failed to build script' });
540
+ }
541
+ });
542
+
543
+ // ─── Stealth ─────────────────────────────────────────────────────────────
544
+
545
+ router.get('/stealth/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
546
+ try {
547
+ const profile = await premium.getStealthProfile(req.params.siteId);
548
+ res.json({ profile });
549
+ } catch (err) {
550
+ res.status(500).json({ error: 'Failed to fetch stealth profile' });
551
+ }
552
+ });
553
+
554
+ router.put('/stealth/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
555
+ try {
556
+ const profile = await premium.upsertStealthProfile(req.params.siteId, req.body);
557
+ res.json({ profile });
558
+ } catch (err) {
559
+ res.status(500).json({ error: 'Failed to update stealth profile' });
560
+ }
561
+ });
562
+
563
+ router.get('/stealth/:siteId/script', authenticateToken, requireSiteOwnership, async (req, res) => {
564
+ try {
565
+ const script = await premium.generateStealthScript(req.params.siteId);
566
+ res.json({ script });
567
+ } catch (err) {
568
+ res.status(500).json({ error: 'Failed to generate stealth script' });
569
+ }
570
+ });
571
+
572
+ // ─── CDN ─────────────────────────────────────────────────────────────────
573
+
574
+ router.get('/cdn/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
575
+ try {
576
+ const config = await premium.getCdnConfig(req.params.siteId);
577
+ res.json({ config });
578
+ } catch (err) {
579
+ res.status(500).json({ error: 'Failed to fetch CDN config' });
580
+ }
581
+ });
582
+
583
+ router.put('/cdn/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
584
+ try {
585
+ const config = await premium.upsertCdnConfig(req.params.siteId, req.body);
586
+ res.json({ config });
587
+ } catch (err) {
588
+ res.status(500).json({ error: 'Failed to update CDN config' });
589
+ }
590
+ });
591
+
592
+ router.get('/cdn/:siteId/stats', authenticateToken, requireSiteOwnership, async (req, res) => {
593
+ try {
594
+ const days = req.query.days ? parseInt(req.query.days) : 30;
595
+ const cdnConfig = await premium.getCdnConfig(req.params.siteId);
596
+ if (!cdnConfig) return res.status(404).json({ error: 'CDN not configured' });
597
+ const stats = await premium.getCdnStats(cdnConfig.id, days);
598
+ res.json({ stats });
599
+ } catch (err) {
600
+ res.status(500).json({ error: 'Failed to fetch CDN stats' });
601
+ }
602
+ });
603
+
604
+ // ─── Audit ───────────────────────────────────────────────────────────────
605
+
606
+ router.get('/audit/:siteId/logs', authenticateToken, requireSiteOwnership, async (req, res) => {
607
+ try {
608
+ const { limit, offset, action, since, until } = req.query;
609
+ const result = await premium.getAuditLogs(req.params.siteId, {
610
+ limit: limit ? parseInt(limit) : undefined,
611
+ offset: offset ? parseInt(offset) : undefined,
612
+ action,
613
+ since,
614
+ until
615
+ });
616
+ res.json(result);
617
+ } catch (err) {
618
+ res.status(500).json({ error: 'Failed to fetch audit logs' });
619
+ }
620
+ });
621
+
622
+ router.get('/audit/:siteId/compliance', authenticateToken, requireSiteOwnership, async (req, res) => {
623
+ try {
624
+ const settings = await premium.getComplianceSettings(req.params.siteId);
625
+ res.json({ settings });
626
+ } catch (err) {
627
+ res.status(500).json({ error: 'Failed to fetch compliance settings' });
628
+ }
629
+ });
630
+
631
+ router.put('/audit/:siteId/compliance', authenticateToken, requireSiteOwnership, async (req, res) => {
632
+ try {
633
+ const settings = await premium.upsertComplianceSettings(req.params.siteId, req.body);
634
+ res.json({ settings });
635
+ } catch (err) {
636
+ res.status(500).json({ error: 'Failed to update compliance settings' });
637
+ }
638
+ });
639
+
640
+ router.get('/audit/:siteId/export', authenticateToken, requireSiteOwnership, async (req, res) => {
641
+ try {
642
+ const { format, since, until } = req.query;
643
+ const result = await premium.exportAuditLogs(req.params.siteId, { format, since, until });
644
+ res.setHeader('Content-Disposition', `attachment; filename="${result.filename}"`);
645
+ res.setHeader('Content-Type', result.contentType);
646
+ res.send(result.data);
647
+ } catch (err) {
648
+ res.status(500).json({ error: 'Failed to export audit logs' });
649
+ }
650
+ });
651
+
652
+ router.post('/audit/:siteId/purge', authenticateToken, requireSiteOwnership, async (req, res) => {
653
+ try {
654
+ const result = await premium.purgeOldLogs(req.params.siteId);
655
+ res.json(result);
656
+ } catch (err) {
657
+ res.status(500).json({ error: 'Failed to purge old logs' });
658
+ }
659
+ });
660
+
661
+ // ─── Sandbox ─────────────────────────────────────────────────────────────
662
+
663
+ router.get('/sandbox/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
664
+ try {
665
+ const sandboxes = await premium.getSandboxes(req.params.siteId);
666
+ res.json({ sandboxes });
667
+ } catch (err) {
668
+ res.status(500).json({ error: 'Failed to fetch sandboxes' });
669
+ }
670
+ });
671
+
672
+ router.post('/sandbox/:siteId', authenticateToken, requireSiteOwnership, async (req, res) => {
673
+ try {
674
+ const { name } = req.body;
675
+ if (!name) return res.status(400).json({ error: 'name is required' });
676
+ const sandbox = await premium.createSandbox(req.params.siteId, { name });
677
+ res.status(201).json({ sandbox });
678
+ } catch (err) {
679
+ res.status(500).json({ error: 'Failed to create sandbox' });
680
+ }
681
+ });
682
+
683
+ router.delete('/sandbox/:siteId/:sandboxId', authenticateToken, requireSiteOwnership, async (req, res) => {
684
+ try {
685
+ const ok = await premium.deleteSandbox(req.params.sandboxId, req.params.siteId);
686
+ if (!ok) return res.status(404).json({ error: 'Sandbox not found' });
687
+ res.json({ success: true });
688
+ } catch (err) {
689
+ res.status(500).json({ error: 'Failed to delete sandbox' });
690
+ }
691
+ });
692
+
693
+ router.post('/sandbox/:siteId/:sandboxId/simulate', authenticateToken, requireSiteOwnership, async (req, res) => {
694
+ try {
695
+ const { agentCount, duration, actionsPerAgent } = req.body;
696
+ if (!agentCount || !duration) return res.status(400).json({ error: 'agentCount and duration are required' });
697
+ const result = await premium.simulateTraffic(req.params.sandboxId, { agentCount, duration, actionsPerAgent });
698
+ res.json({ result });
699
+ } catch (err) {
700
+ res.status(500).json({ error: 'Failed to simulate traffic' });
701
+ }
702
+ });
703
+
704
+ router.post('/sandbox/:siteId/:sandboxId/benchmark', authenticateToken, requireSiteOwnership, async (req, res) => {
705
+ try {
706
+ const { benchmarkType } = req.body;
707
+ if (!benchmarkType) return res.status(400).json({ error: 'benchmarkType is required' });
708
+ const result = await premium.runBenchmark(req.params.sandboxId, { benchmarkType });
709
+ res.json({ result });
710
+ } catch (err) {
711
+ res.status(500).json({ error: 'Failed to run benchmark' });
712
+ }
713
+ });
714
+
715
+ router.get('/sandbox/:siteId/:sandboxId/benchmarks', authenticateToken, requireSiteOwnership, async (req, res) => {
716
+ try {
717
+ const benchmarks = await premium.getBenchmarks(req.params.sandboxId);
718
+ res.json({ benchmarks });
719
+ } catch (err) {
720
+ res.status(500).json({ error: 'Failed to fetch benchmarks' });
721
+ }
722
+ });
723
+
724
+ module.exports = router;