wiki-plugin-lucille 0.0.1 → 0.0.2
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/client/lucille.js +156 -42
- package/package.json +1 -1
- package/server/server.js +12 -3
package/client/lucille.js
CHANGED
|
@@ -22,52 +22,16 @@ window.plugins.lucille = (function() {
|
|
|
22
22
|
const price = item.price || 0;
|
|
23
23
|
const playerBase = item.playerBase || '/plugin/lucille';
|
|
24
24
|
|
|
25
|
-
// Check owner status and render
|
|
25
|
+
// Check owner status and render setup dashboard if owner
|
|
26
26
|
fetch('/plugin/lucille/setup/status', { credentials: 'include' })
|
|
27
27
|
.then(r => r.json())
|
|
28
28
|
.then(data => {
|
|
29
29
|
if (!data.isOwner) return;
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
? '<a href="/plugin/lucille/setup/stripe" target="_blank" style="color:#fb0;font-size:0.75rem;">⚠️ Complete Stripe setup →</a>'
|
|
36
|
-
: '';
|
|
37
|
-
configDiv.innerHTML = `
|
|
38
|
-
<div style="font-size:0.8rem;color:#a78bfa;margin-bottom:8px;font-weight:600;">Allyabase connection (owner only)</div>
|
|
39
|
-
<div style="display:flex;gap:6px;align-items:center;">
|
|
40
|
-
<input id="luc-url-input" value="${escapeHtml(data.allyabaseUrl || '')}" placeholder="https://dev.allyabase.com"
|
|
41
|
-
style="flex:1;background:#0d001a;border:1px solid rgba(167,139,250,0.3);border-radius:4px;padding:6px 8px;color:#e0d0ff;font-size:0.8rem;">
|
|
42
|
-
<button id="luc-url-btn"
|
|
43
|
-
style="background:#7c3aed;border:none;border-radius:4px;padding:6px 12px;color:white;cursor:pointer;font-size:0.8rem;white-space:nowrap;">
|
|
44
|
-
Save
|
|
45
|
-
</button>
|
|
46
|
-
</div>
|
|
47
|
-
<div id="luc-url-status" style="margin-top:6px;font-size:0.75rem;"></div>
|
|
48
|
-
${stripeStatus ? '<div style="margin-top:6px;">' + stripeStatus + '</div>' : ''}`;
|
|
49
|
-
$item.append(configDiv);
|
|
50
|
-
configDiv.querySelector('#luc-url-btn').addEventListener('click', function() {
|
|
51
|
-
const input = configDiv.querySelector('#luc-url-input');
|
|
52
|
-
const btn = configDiv.querySelector('#luc-url-btn');
|
|
53
|
-
const status = configDiv.querySelector('#luc-url-status');
|
|
54
|
-
const base = (input.value || '').trim().replace(/\/$/, '');
|
|
55
|
-
if (!base) { status.textContent = 'Enter an allyabase URL first'; return; }
|
|
56
|
-
btn.disabled = true; btn.textContent = 'Saving…';
|
|
57
|
-
fetch('/plugin/lucille/setup', {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
credentials: 'include',
|
|
60
|
-
headers: { 'Content-Type': 'application/json' },
|
|
61
|
-
body: JSON.stringify({ allyabaseUrl: base })
|
|
62
|
-
})
|
|
63
|
-
.then(r => r.json())
|
|
64
|
-
.then(d => {
|
|
65
|
-
if (d.error) { status.innerHTML = '<span style="color:#f55;">' + escapeHtml(d.error) + '</span>'; return; }
|
|
66
|
-
status.innerHTML = '<span style="color:#0e0;">Saved</span>';
|
|
67
|
-
})
|
|
68
|
-
.catch(e => { status.innerHTML = '<span style="color:#f55;">' + escapeHtml(e.message) + '</span>'; })
|
|
69
|
-
.finally(() => { btn.disabled = false; btn.textContent = 'Save'; });
|
|
70
|
-
});
|
|
30
|
+
const panel = document.createElement('div');
|
|
31
|
+
panel.style.cssText = 'font-family:system-ui;margin-top:8px;';
|
|
32
|
+
panel.innerHTML = lucilleOwnerHTML(data);
|
|
33
|
+
$item.append(panel);
|
|
34
|
+
attachLucilleOwnerListeners(panel, data);
|
|
71
35
|
})
|
|
72
36
|
.catch(() => {});
|
|
73
37
|
|
|
@@ -160,6 +124,156 @@ window.plugins.lucille = (function() {
|
|
|
160
124
|
});
|
|
161
125
|
}
|
|
162
126
|
|
|
127
|
+
// ── Owner dashboard ───────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
function dot(ok, warn) {
|
|
130
|
+
const c = ok ? '#0e0' : warn ? '#fb0' : '#f55';
|
|
131
|
+
return `<span style="color:${c};font-size:14px;line-height:1;">●</span>`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function lucilleOwnerHTML(d) {
|
|
135
|
+
const spacesOk = d.hasSpacesKey && d.spacesBucket;
|
|
136
|
+
const spacesLabel = spacesOk
|
|
137
|
+
? `${escapeHtml(d.spacesRegion)} / <strong>${escapeHtml(d.spacesBucket)}</strong>${d.hasCdnEndpoint ? ' + CDN' : ''}`
|
|
138
|
+
: 'Not configured';
|
|
139
|
+
const trackerOk = !!d.trackerHost;
|
|
140
|
+
const trackerLabel = trackerOk
|
|
141
|
+
? `ws://${escapeHtml(d.trackerHost)}:${d.trackerPort}`
|
|
142
|
+
: 'No public hostname set';
|
|
143
|
+
|
|
144
|
+
const allyOk = !!d.allyabaseUrl;
|
|
145
|
+
const stripeOk = d.stripeOnboarded;
|
|
146
|
+
const addiePending = d.serverAddieReady && !stripeOk;
|
|
147
|
+
|
|
148
|
+
const serviceColor = d.running ? '#0e0' : d.configured ? '#fb0' : '#f55';
|
|
149
|
+
const serviceLabel = d.running ? 'Running' : d.configured ? 'Stopped — restart wiki' : 'Not configured';
|
|
150
|
+
|
|
151
|
+
return `
|
|
152
|
+
<div style="border:2px solid #7c3aed;border-radius:10px;padding:14px;background:#0d001a;color:#e0d0ff;">
|
|
153
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
|
154
|
+
<span style="font-size:1rem;">🎬</span>
|
|
155
|
+
<strong style="color:#a78bfa;font-size:0.95rem;">Lucille</strong>
|
|
156
|
+
<span style="margin-left:auto;font-size:0.7rem;color:#5a3080;background:rgba(124,58,237,.15);border:1px solid rgba(124,58,237,.3);border-radius:4px;padding:2px 6px;">owner</span>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Service status -->
|
|
160
|
+
<div style="background:rgba(167,139,250,0.06);border:1px solid rgba(167,139,250,0.15);border-radius:6px;padding:8px 12px;margin-bottom:12px;font-size:0.82rem;">
|
|
161
|
+
<span style="color:#7060a0;">Service: </span>
|
|
162
|
+
<span style="color:${serviceColor};font-weight:600;">${dot(d.running, d.configured)} ${serviceLabel}</span>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Config checklist -->
|
|
166
|
+
<div style="font-size:0.8rem;margin-bottom:12px;">
|
|
167
|
+
|
|
168
|
+
<!-- DO Spaces row -->
|
|
169
|
+
<div style="display:flex;align-items:baseline;gap:8px;padding:6px 0;border-bottom:1px solid rgba(90,48,128,0.25);">
|
|
170
|
+
<span style="flex:0 0 14px;">${dot(spacesOk)}</span>
|
|
171
|
+
<span style="color:#a080d0;flex:0 0 110px;">DO Spaces</span>
|
|
172
|
+
<span style="color:${spacesOk ? '#c89aff' : '#7060a0'};">${spacesLabel}</span>
|
|
173
|
+
<a href="/plugin/lucille/setup" target="_blank"
|
|
174
|
+
style="margin-left:auto;color:#7c3aed;font-size:0.72rem;white-space:nowrap;text-decoration:none;">
|
|
175
|
+
${spacesOk ? 'Edit ↗' : 'Configure ↗'}
|
|
176
|
+
</a>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<!-- Tracker row -->
|
|
180
|
+
<div style="display:flex;align-items:baseline;gap:8px;padding:6px 0;border-bottom:1px solid rgba(90,48,128,0.25);">
|
|
181
|
+
<span style="flex:0 0 14px;">${dot(trackerOk, true)}</span>
|
|
182
|
+
<span style="color:#a080d0;flex:0 0 110px;">Tracker</span>
|
|
183
|
+
<span style="color:${trackerOk ? '#c89aff' : '#7060a0'};">${trackerLabel}</span>
|
|
184
|
+
<a href="/plugin/lucille/setup" target="_blank"
|
|
185
|
+
style="margin-left:auto;color:#7c3aed;font-size:0.72rem;white-space:nowrap;text-decoration:none;">
|
|
186
|
+
${trackerOk ? 'Edit ↗' : 'Configure ↗'}
|
|
187
|
+
</a>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<!-- Allyabase row -->
|
|
191
|
+
<div style="padding:6px 0;border-bottom:1px solid rgba(90,48,128,0.25);">
|
|
192
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:${allyOk ? 0 : 6}px;">
|
|
193
|
+
<span style="flex:0 0 14px;">${dot(allyOk)}</span>
|
|
194
|
+
<span style="color:#a080d0;flex:0 0 110px;">Allyabase</span>
|
|
195
|
+
${allyOk
|
|
196
|
+
? `<span style="color:#c89aff;font-size:0.78rem;">${escapeHtml(d.allyabaseUrl)}</span>`
|
|
197
|
+
: '<span style="color:#7060a0;">Not set</span>'
|
|
198
|
+
}
|
|
199
|
+
</div>
|
|
200
|
+
${!allyOk ? `
|
|
201
|
+
<div style="display:flex;gap:6px;margin-top:4px;padding-left:22px;">
|
|
202
|
+
<input id="luc-url-input" value="" placeholder="https://dev.allyabase.com"
|
|
203
|
+
style="flex:1;background:#1a0033;border:1px solid #5a3080;border-radius:4px;padding:5px 8px;color:#e0d0ff;font-size:0.78rem;">
|
|
204
|
+
<button id="luc-url-btn"
|
|
205
|
+
style="background:#7c3aed;border:none;border-radius:4px;padding:5px 10px;color:white;cursor:pointer;font-size:0.78rem;white-space:nowrap;">
|
|
206
|
+
Save
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
<div id="luc-url-status" style="margin-top:4px;font-size:0.72rem;padding-left:22px;min-height:1em;"></div>` : `
|
|
210
|
+
<div style="display:flex;gap:6px;margin-top:4px;padding-left:22px;">
|
|
211
|
+
<input id="luc-url-input" value="${escapeHtml(d.allyabaseUrl)}" placeholder="https://dev.allyabase.com"
|
|
212
|
+
style="flex:1;background:#1a0033;border:1px solid #5a3080;border-radius:4px;padding:5px 8px;color:#e0d0ff;font-size:0.78rem;">
|
|
213
|
+
<button id="luc-url-btn"
|
|
214
|
+
style="background:#5a3080;border:none;border-radius:4px;padding:5px 10px;color:#c89aff;cursor:pointer;font-size:0.78rem;white-space:nowrap;">
|
|
215
|
+
Update
|
|
216
|
+
</button>
|
|
217
|
+
</div>
|
|
218
|
+
<div id="luc-url-status" style="margin-top:4px;font-size:0.72rem;padding-left:22px;min-height:1em;"></div>`}
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<!-- Stripe row -->
|
|
222
|
+
<div style="display:flex;align-items:center;gap:8px;padding:6px 0;">
|
|
223
|
+
<span style="flex:0 0 14px;">${dot(stripeOk, addiePending)}</span>
|
|
224
|
+
<span style="color:#a080d0;flex:0 0 110px;">Stripe</span>
|
|
225
|
+
${stripeOk
|
|
226
|
+
? '<span style="color:#0e0;font-size:0.78rem;">Payouts enabled — tier upgrades will pay you</span>'
|
|
227
|
+
: addiePending
|
|
228
|
+
? `<a href="/plugin/lucille/setup/stripe" target="_blank"
|
|
229
|
+
style="color:#fbbf24;font-size:0.78rem;text-decoration:none;">
|
|
230
|
+
⚠️ Complete Stripe onboarding →
|
|
231
|
+
</a>`
|
|
232
|
+
: '<span style="color:#7060a0;font-size:0.78rem;">Save Allyabase URL first</span>'
|
|
233
|
+
}
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<!-- Full setup link -->
|
|
239
|
+
<a href="/plugin/lucille/setup" target="_blank"
|
|
240
|
+
style="display:block;text-align:center;padding:7px;background:rgba(124,58,237,.2);border:1px solid rgba(124,58,237,.4);border-radius:6px;color:#c89aff;font-size:0.8rem;font-weight:600;text-decoration:none;">
|
|
241
|
+
Open full setup page ↗
|
|
242
|
+
</a>
|
|
243
|
+
<div style="margin-top:8px;font-size:0.72rem;color:#5a3080;text-align:center;">
|
|
244
|
+
DO Spaces credentials, tiers, federation peers, and tracker config are all on the setup page.
|
|
245
|
+
</div>
|
|
246
|
+
</div>`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function attachLucilleOwnerListeners(panel, data) {
|
|
250
|
+
const urlBtn = panel.querySelector('#luc-url-btn');
|
|
251
|
+
if (!urlBtn) return;
|
|
252
|
+
urlBtn.addEventListener('click', function() {
|
|
253
|
+
const input = panel.querySelector('#luc-url-input');
|
|
254
|
+
const status = panel.querySelector('#luc-url-status');
|
|
255
|
+
const base = (input.value || '').trim().replace(/\/$/, '');
|
|
256
|
+
if (!base) { status.textContent = 'Enter a URL first.'; return; }
|
|
257
|
+
urlBtn.disabled = true; urlBtn.textContent = 'Saving…';
|
|
258
|
+
fetch('/plugin/lucille/setup', {
|
|
259
|
+
method: 'POST', credentials: 'include',
|
|
260
|
+
headers: { 'Content-Type': 'application/json' },
|
|
261
|
+
body: JSON.stringify({ allyabaseUrl: base })
|
|
262
|
+
})
|
|
263
|
+
.then(r => r.json())
|
|
264
|
+
.then(d => {
|
|
265
|
+
if (d.error) { status.innerHTML = '<span style="color:#f55;">' + escapeHtml(d.error) + '</span>'; return; }
|
|
266
|
+
status.innerHTML = '<span style="color:#0e0;">✅ Saved</span>';
|
|
267
|
+
// Re-fetch and re-render
|
|
268
|
+
fetch('/plugin/lucille/setup/status', { credentials: 'include' })
|
|
269
|
+
.then(r => r.json())
|
|
270
|
+
.then(fresh => { if (fresh.isOwner) { panel.innerHTML = lucilleOwnerHTML(fresh); attachLucilleOwnerListeners(panel, fresh); } });
|
|
271
|
+
})
|
|
272
|
+
.catch(e => { status.innerHTML = '<span style="color:#f55;">' + escapeHtml(e.message) + '</span>'; })
|
|
273
|
+
.finally(() => { urlBtn.disabled = false; urlBtn.textContent = data.allyabaseUrl ? 'Update' : 'Save'; });
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
163
277
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
164
278
|
function escapeHtml(str) {
|
|
165
279
|
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-plugin-lucille",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Federated Wiki plugin — P2P video hosting via Lucille (WebTorrent + DO Spaces)",
|
|
5
5
|
"keywords": ["wiki", "plugin", "video", "webtorrent", "planet-nine"],
|
|
6
6
|
"author": "Planet Nine",
|
package/server/server.js
CHANGED
|
@@ -775,9 +775,18 @@ async function startServer(params) {
|
|
|
775
775
|
tiers: cfg.tiers,
|
|
776
776
|
federationPeers: cfg.federationPeers,
|
|
777
777
|
isOwner,
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
778
|
+
// Owner-only fields
|
|
779
|
+
...(isOwner && {
|
|
780
|
+
allyabaseUrl,
|
|
781
|
+
serverAddieReady: !!(cfg.serverAddie && cfg.serverAddie.uuid),
|
|
782
|
+
stripeOnboarded: !!cfg.stripeOnboarded,
|
|
783
|
+
hasSpacesKey: !!cfg.spacesKey,
|
|
784
|
+
spacesBucket: cfg.spacesBucket || '',
|
|
785
|
+
spacesRegion: cfg.spacesRegion || 'nyc3',
|
|
786
|
+
trackerHost: cfg.trackerHost || '',
|
|
787
|
+
trackerPort: cfg.trackerPort || 8000,
|
|
788
|
+
hasCdnEndpoint: !!cfg.spacesCdnEndpoint
|
|
789
|
+
})
|
|
781
790
|
});
|
|
782
791
|
});
|
|
783
792
|
|