@intranefr/superbackend 1.6.6 → 1.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +4 -0
- package/README.md +18 -0
- package/package.json +8 -2
- package/public/js/admin-superdemos.js +396 -0
- package/public/sdk/superdemos.iife.js +614 -0
- package/public/superdemos-qa.html +324 -0
- package/sdk/superdemos/browser/src/index.js +719 -0
- package/src/cli/agent-chat.js +369 -0
- package/src/cli/agent-list.js +42 -0
- package/src/controllers/adminAgentsChat.controller.js +172 -0
- package/src/controllers/adminSuperDemos.controller.js +382 -0
- package/src/controllers/superDemosPublic.controller.js +126 -0
- package/src/middleware.js +102 -19
- package/src/models/BlogAutomationLock.js +4 -4
- package/src/models/BlogPost.js +16 -16
- package/src/models/CacheEntry.js +17 -6
- package/src/models/JsonConfig.js +2 -4
- package/src/models/RateLimitMetricBucket.js +10 -5
- package/src/models/SuperDemo.js +38 -0
- package/src/models/SuperDemoProject.js +32 -0
- package/src/models/SuperDemoStep.js +27 -0
- package/src/routes/adminAgents.routes.js +10 -0
- package/src/routes/adminMarkdowns.routes.js +3 -0
- package/src/routes/adminSuperDemos.routes.js +31 -0
- package/src/routes/superDemos.routes.js +9 -0
- package/src/services/auditLogger.js +75 -37
- package/src/services/email.service.js +18 -3
- package/src/services/superDemosAuthoringSessions.service.js +132 -0
- package/src/services/superDemosWs.service.js +164 -0
- package/src/services/terminalsWs.service.js +35 -3
- package/src/utils/rbac/rightsRegistry.js +2 -0
- package/views/admin-agents.ejs +261 -11
- package/views/admin-dashboard.ejs +78 -8
- package/views/admin-superdemos.ejs +335 -0
- package/views/admin-terminals.ejs +462 -34
- package/views/partials/admin/agents-chat.ejs +80 -0
- package/views/partials/dashboard/nav-items.ejs +1 -0
- package/views/partials/dashboard/tab-bar.ejs +6 -0
- package/cookies.txt +0 -6
- package/cookies1.txt +0 -6
- package/cookies2.txt +0 -6
- package/cookies3.txt +0 -6
- package/cookies4.txt +0 -5
- package/cookies_old.txt +0 -5
- package/cookies_old_test.txt +0 -6
- package/cookies_super.txt +0 -5
- package/cookies_super_test.txt +0 -6
- package/cookies_test.txt +0 -6
- package/test-access.js +0 -63
- package/test-iframe-fix.html +0 -63
- package/test-iframe.html +0 -14
|
@@ -0,0 +1,324 @@
|
|
|
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>SuperDemos QA Playground</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family:
|
|
10
|
+
Inter,
|
|
11
|
+
system-ui,
|
|
12
|
+
-apple-system,
|
|
13
|
+
Segoe UI,
|
|
14
|
+
Roboto,
|
|
15
|
+
sans-serif;
|
|
16
|
+
margin: 0;
|
|
17
|
+
background: #f6f7fb;
|
|
18
|
+
color: #111827;
|
|
19
|
+
}
|
|
20
|
+
.wrap {
|
|
21
|
+
max-width: 1100px;
|
|
22
|
+
margin: 0 auto;
|
|
23
|
+
padding: 20px;
|
|
24
|
+
}
|
|
25
|
+
.card {
|
|
26
|
+
background: #fff;
|
|
27
|
+
border: 1px solid #e5e7eb;
|
|
28
|
+
border-radius: 12px;
|
|
29
|
+
padding: 14px;
|
|
30
|
+
margin-bottom: 14px;
|
|
31
|
+
}
|
|
32
|
+
.row {
|
|
33
|
+
display: grid;
|
|
34
|
+
grid-template-columns: 1fr 1fr;
|
|
35
|
+
gap: 10px;
|
|
36
|
+
}
|
|
37
|
+
label {
|
|
38
|
+
font-size: 12px;
|
|
39
|
+
color: #374151;
|
|
40
|
+
display: block;
|
|
41
|
+
margin-bottom: 4px;
|
|
42
|
+
}
|
|
43
|
+
input,
|
|
44
|
+
textarea,
|
|
45
|
+
select {
|
|
46
|
+
width: 100%;
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
border: 1px solid #d1d5db;
|
|
49
|
+
border-radius: 8px;
|
|
50
|
+
padding: 8px 10px;
|
|
51
|
+
font-size: 13px;
|
|
52
|
+
}
|
|
53
|
+
button {
|
|
54
|
+
border: 0;
|
|
55
|
+
border-radius: 8px;
|
|
56
|
+
padding: 9px 12px;
|
|
57
|
+
font-size: 13px;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
}
|
|
60
|
+
.btn {
|
|
61
|
+
background: #111827;
|
|
62
|
+
color: #fff;
|
|
63
|
+
}
|
|
64
|
+
.btn.secondary {
|
|
65
|
+
background: #e5e7eb;
|
|
66
|
+
color: #111827;
|
|
67
|
+
}
|
|
68
|
+
.note {
|
|
69
|
+
font-size: 12px;
|
|
70
|
+
color: #4b5563;
|
|
71
|
+
}
|
|
72
|
+
.stage {
|
|
73
|
+
display: grid;
|
|
74
|
+
grid-template-columns: 260px 1fr;
|
|
75
|
+
gap: 12px;
|
|
76
|
+
margin-top: 8px;
|
|
77
|
+
}
|
|
78
|
+
.sidebar {
|
|
79
|
+
background: #f9fafb;
|
|
80
|
+
border: 1px solid #e5e7eb;
|
|
81
|
+
border-radius: 10px;
|
|
82
|
+
padding: 10px;
|
|
83
|
+
}
|
|
84
|
+
.panel {
|
|
85
|
+
min-height: 460px;
|
|
86
|
+
background: #fff;
|
|
87
|
+
border: 1px solid #e5e7eb;
|
|
88
|
+
border-radius: 10px;
|
|
89
|
+
padding: 14px;
|
|
90
|
+
}
|
|
91
|
+
.fake-card {
|
|
92
|
+
border: 1px solid #e5e7eb;
|
|
93
|
+
border-radius: 10px;
|
|
94
|
+
padding: 12px;
|
|
95
|
+
margin-bottom: 10px;
|
|
96
|
+
}
|
|
97
|
+
.muted {
|
|
98
|
+
color: #6b7280;
|
|
99
|
+
font-size: 12px;
|
|
100
|
+
}
|
|
101
|
+
#log {
|
|
102
|
+
height: 180px;
|
|
103
|
+
overflow: auto;
|
|
104
|
+
background: #0b1220;
|
|
105
|
+
color: #d1e7ff;
|
|
106
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
107
|
+
font-size: 12px;
|
|
108
|
+
border-radius: 10px;
|
|
109
|
+
padding: 10px;
|
|
110
|
+
}
|
|
111
|
+
@media (max-width: 900px) {
|
|
112
|
+
.row,
|
|
113
|
+
.stage {
|
|
114
|
+
grid-template-columns: 1fr;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
118
|
+
</head>
|
|
119
|
+
<body>
|
|
120
|
+
<div class="wrap">
|
|
121
|
+
<div class="card">
|
|
122
|
+
<h2 style="margin: 0 0 8px 0">SuperDemos QA Playground</h2>
|
|
123
|
+
<div class="note">
|
|
124
|
+
Use this page as target URL from Admin SuperDemos authoring. It also
|
|
125
|
+
supports live-mode playback with manual config below.
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div class="card">
|
|
130
|
+
<div class="row">
|
|
131
|
+
<div>
|
|
132
|
+
<label>API URL</label>
|
|
133
|
+
<input id="apiUrl" />
|
|
134
|
+
</div>
|
|
135
|
+
<div>
|
|
136
|
+
<label>Project ID</label>
|
|
137
|
+
<input id="projectId" placeholder="sdp_..." />
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
<div style="margin-top: 10px">
|
|
141
|
+
<label style="display: flex; align-items: center; gap: 8px; margin-bottom: 0">
|
|
142
|
+
<input id="force" type="checkbox" style="width: auto" />
|
|
143
|
+
<span>Force replay even if already seen</span>
|
|
144
|
+
</label>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="row" style="margin-top: 10px">
|
|
147
|
+
<div>
|
|
148
|
+
<label>Project API key (private projects only)</label>
|
|
149
|
+
<input id="apiKey" placeholder="optional" />
|
|
150
|
+
</div>
|
|
151
|
+
<div>
|
|
152
|
+
<label>Mode</label>
|
|
153
|
+
<select id="mode">
|
|
154
|
+
<option value="live">live</option>
|
|
155
|
+
<option value="author">
|
|
156
|
+
author (usually auto via query params)
|
|
157
|
+
</option>
|
|
158
|
+
</select>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
<div
|
|
162
|
+
style="display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap"
|
|
163
|
+
>
|
|
164
|
+
<button class="btn" id="btnInit">Init SuperDemos</button>
|
|
165
|
+
<button class="btn secondary" id="btnSave">Save config</button>
|
|
166
|
+
<button class="btn secondary" id="btnResetSeen">
|
|
167
|
+
Reset seen flags
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="note" style="margin-top: 8px">
|
|
171
|
+
Author mode is automatically enabled when URL includes
|
|
172
|
+
<code>sd_session</code> and <code>sd_token</code>.
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="card">
|
|
177
|
+
<div class="stage">
|
|
178
|
+
<aside class="sidebar">
|
|
179
|
+
<div class="muted">Navigation</div>
|
|
180
|
+
<button
|
|
181
|
+
id="menu-home"
|
|
182
|
+
class="btn secondary"
|
|
183
|
+
style="width: 100%; margin-top: 8px"
|
|
184
|
+
>
|
|
185
|
+
Home
|
|
186
|
+
</button>
|
|
187
|
+
<button
|
|
188
|
+
id="menu-billing"
|
|
189
|
+
class="btn secondary"
|
|
190
|
+
style="width: 100%; margin-top: 8px"
|
|
191
|
+
>
|
|
192
|
+
Billing
|
|
193
|
+
</button>
|
|
194
|
+
<button
|
|
195
|
+
id="menu-settings"
|
|
196
|
+
class="btn secondary"
|
|
197
|
+
style="width: 100%; margin-top: 8px"
|
|
198
|
+
>
|
|
199
|
+
Settings
|
|
200
|
+
</button>
|
|
201
|
+
</aside>
|
|
202
|
+
<main class="panel">
|
|
203
|
+
<h3 id="hero-title" style="margin-top: 0">Welcome to QA Product</h3>
|
|
204
|
+
<p id="hero-subtitle" class="muted">
|
|
205
|
+
This fake app contains stable selectors for SuperDemos authoring
|
|
206
|
+
and live playback.
|
|
207
|
+
</p>
|
|
208
|
+
<div class="fake-card" data-testid="plan-card">
|
|
209
|
+
<strong id="plan-name">Pro Plan</strong>
|
|
210
|
+
<p class="muted">
|
|
211
|
+
Includes unlimited projects and priority support.
|
|
212
|
+
</p>
|
|
213
|
+
<button id="upgrade-btn" class="btn">Upgrade now</button>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="fake-card" data-testid="invoice-card">
|
|
216
|
+
<strong>Invoice #A-1024</strong>
|
|
217
|
+
<p class="muted">Amount due: $149</p>
|
|
218
|
+
<button id="pay-now-btn" class="btn secondary">Pay now</button>
|
|
219
|
+
</div>
|
|
220
|
+
<label for="coupon-input">Coupon</label>
|
|
221
|
+
<input id="coupon-input" placeholder="SPRING20" />
|
|
222
|
+
<div style="margin-top: 10px">
|
|
223
|
+
<button id="apply-coupon-btn" class="btn secondary">
|
|
224
|
+
Apply coupon
|
|
225
|
+
</button>
|
|
226
|
+
</div>
|
|
227
|
+
</main>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div class="card">
|
|
232
|
+
<h3 style="margin-top: 0">Runtime log</h3>
|
|
233
|
+
<pre id="log"></pre>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<script src="/public/sdk/superdemos.iife.js"></script>
|
|
238
|
+
<script>
|
|
239
|
+
(function () {
|
|
240
|
+
const logEl = document.getElementById("log");
|
|
241
|
+
const append = (msg) => {
|
|
242
|
+
const line = "[" + new Date().toISOString() + "] " + msg;
|
|
243
|
+
logEl.textContent += line + "\n";
|
|
244
|
+
logEl.scrollTop = logEl.scrollHeight;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const defaultApiUrl = window.location.origin;
|
|
248
|
+
const keys = {
|
|
249
|
+
apiUrl: "superdemos.qa.apiUrl",
|
|
250
|
+
projectId: "superdemos.qa.projectId",
|
|
251
|
+
apiKey: "superdemos.qa.apiKey",
|
|
252
|
+
mode: "superdemos.qa.mode",
|
|
253
|
+
force: "superdemos.qa.force",
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const apiUrlEl = document.getElementById("apiUrl");
|
|
257
|
+
const projectIdEl = document.getElementById("projectId");
|
|
258
|
+
const apiKeyEl = document.getElementById("apiKey");
|
|
259
|
+
const modeEl = document.getElementById("mode");
|
|
260
|
+
const forceEl = document.getElementById("force");
|
|
261
|
+
|
|
262
|
+
apiUrlEl.value = localStorage.getItem(keys.apiUrl) || defaultApiUrl;
|
|
263
|
+
projectIdEl.value = localStorage.getItem(keys.projectId) || "";
|
|
264
|
+
apiKeyEl.value = localStorage.getItem(keys.apiKey) || "";
|
|
265
|
+
modeEl.value = localStorage.getItem(keys.mode) || "live";
|
|
266
|
+
forceEl.checked = localStorage.getItem(keys.force) === "1";
|
|
267
|
+
|
|
268
|
+
document.getElementById("btnSave").addEventListener("click", () => {
|
|
269
|
+
localStorage.setItem(keys.apiUrl, apiUrlEl.value || defaultApiUrl);
|
|
270
|
+
localStorage.setItem(keys.projectId, projectIdEl.value || "");
|
|
271
|
+
localStorage.setItem(keys.apiKey, apiKeyEl.value || "");
|
|
272
|
+
localStorage.setItem(keys.mode, modeEl.value || "live");
|
|
273
|
+
localStorage.setItem(keys.force, forceEl.checked ? "1" : "0");
|
|
274
|
+
append("Saved config.");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
document
|
|
278
|
+
.getElementById("btnResetSeen")
|
|
279
|
+
.addEventListener("click", () => {
|
|
280
|
+
const toDelete = [];
|
|
281
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
282
|
+
const k = localStorage.key(i);
|
|
283
|
+
if (k && k.startsWith("superdemos.seen.")) toDelete.push(k);
|
|
284
|
+
}
|
|
285
|
+
toDelete.forEach((k) => localStorage.removeItem(k));
|
|
286
|
+
append("Cleared seen keys: " + toDelete.length);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
document
|
|
290
|
+
.getElementById("btnInit")
|
|
291
|
+
.addEventListener("click", async () => {
|
|
292
|
+
try {
|
|
293
|
+
append("Init requested...");
|
|
294
|
+
const result = await window.superDemos.init({
|
|
295
|
+
apiUrl: apiUrlEl.value || defaultApiUrl,
|
|
296
|
+
projectId: projectIdEl.value,
|
|
297
|
+
apiKey: apiKeyEl.value || null,
|
|
298
|
+
mode: modeEl.value || "live",
|
|
299
|
+
force: forceEl.checked,
|
|
300
|
+
});
|
|
301
|
+
append("Init result: " + JSON.stringify(result));
|
|
302
|
+
} catch (e) {
|
|
303
|
+
append("Init error: " + (e && e.message ? e.message : String(e)));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// some interactions to make QA page feel like an app
|
|
308
|
+
const clickLog = (id, text) => {
|
|
309
|
+
const el = document.getElementById(id);
|
|
310
|
+
if (!el) return;
|
|
311
|
+
el.addEventListener("click", () => append(text));
|
|
312
|
+
};
|
|
313
|
+
clickLog("upgrade-btn", "Clicked upgrade");
|
|
314
|
+
clickLog("pay-now-btn", "Clicked pay now");
|
|
315
|
+
clickLog("apply-coupon-btn", "Clicked apply coupon");
|
|
316
|
+
clickLog("menu-home", "Navigated Home");
|
|
317
|
+
clickLog("menu-billing", "Navigated Billing");
|
|
318
|
+
clickLog("menu-settings", "Navigated Settings");
|
|
319
|
+
|
|
320
|
+
append("QA page loaded.");
|
|
321
|
+
})();
|
|
322
|
+
</script>
|
|
323
|
+
</body>
|
|
324
|
+
</html>
|