feat: Chrome extension download + install guide in admin panel
- GET /api/extension/download — streams chrome-extension/ as a .zip using archiver
- Admin → 🧩 Extension tab with:
- One-click download button (fetches zip via auth'd API, triggers browser save)
- 5-step install guide: unzip, chrome://extensions, developer mode, load unpacked, configure
- UDP port-forwarding reminder note
- Added archiver dependency to package.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fbec32dcc4
commit
895145e1ed
3 changed files with 99 additions and 1 deletions
|
|
@ -12,6 +12,7 @@
|
|||
"@aws-sdk/lib-storage": "^3.650.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.650.0",
|
||||
"@smithy/node-http-handler": "^3.2.0",
|
||||
"archiver": "^7.0.1",
|
||||
"express": "^4.21.0",
|
||||
"multer": "^1.4.5-lts.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -320,6 +320,15 @@ body:not([data-theme="light"]) .header-wordmark{filter:invert(1)}
|
|||
.admin-tab.active{color:var(--blue-bright);border-bottom-color:var(--blue-bright)}
|
||||
.admin-panel{display:none}
|
||||
.admin-panel.active{display:block}
|
||||
/* EXTENSION INSTALL GUIDE */
|
||||
.ext-steps{display:flex;flex-direction:column;gap:.88rem;margin-bottom:1.5rem}
|
||||
.ext-step{display:flex;gap:1rem;align-items:flex-start}
|
||||
.ext-step-num{flex-shrink:0;width:28px;height:28px;border-radius:50%;background:var(--blue);color:#fff;font-size:.78rem;font-weight:700;display:flex;align-items:center;justify-content:center;margin-top:.1rem}
|
||||
.ext-step-body{flex:1}
|
||||
.ext-step-body strong{display:block;font-size:.88rem;color:var(--text-primary);margin-bottom:.25rem}
|
||||
.ext-step-body p{font-size:.82rem;color:var(--text-secondary);margin:0;line-height:1.55}
|
||||
.ext-step-body code{background:var(--bg-secondary);border:1px solid var(--border);border-radius:4px;padding:.1rem .4rem;font-family:'Courier New',monospace;font-size:.8rem;color:var(--blue-bright)}
|
||||
.ext-note{background:var(--bg-secondary);border:1px solid var(--border);border-left:3px solid var(--dragon);border-radius:8px;padding:1rem 1.1rem;font-size:.82rem;color:var(--text-secondary);line-height:1.6}
|
||||
|
||||
/* FORMS */
|
||||
.form-group{margin-bottom:1rem}
|
||||
|
|
@ -520,6 +529,7 @@ body:not([data-theme="light"]) .header-wordmark{filter:invert(1)}
|
|||
<div class="admin-tab active" onclick="switchAdminTab('s3')">S3 Storage</div>
|
||||
<div class="admin-tab" onclick="switchAdminTab('relay')">UDP Relay</div>
|
||||
<div class="admin-tab" onclick="switchAdminTab('ampp')">AMPP</div>
|
||||
<div class="admin-tab" onclick="switchAdminTab('extension')">🧩 Extension</div>
|
||||
<div class="admin-tab" onclick="switchAdminTab('users')">Users</div>
|
||||
<div class="admin-tab" onclick="switchAdminTab('folders')">Folders</div>
|
||||
</div>
|
||||
|
|
@ -578,6 +588,58 @@ body:not([data-theme="light"]) .header-wordmark{filter:invert(1)}
|
|||
<div class="status-msg" id="ampp-status"></div>
|
||||
</div>
|
||||
|
||||
<!-- Extension -->
|
||||
<div class="admin-panel" id="admin-extension">
|
||||
<div class="section-title">Chrome Extension — UDP Upload</div>
|
||||
<p style="color:var(--text-secondary);margin-bottom:1.5rem;line-height:1.6">The Dragon Wind Chrome extension enables UDP-accelerated uploads directly from your browser. Download the extension package below, then follow the steps to install it.</p>
|
||||
|
||||
<button class="btn-primary" style="margin-bottom:2rem" onclick="downloadExtension()">⬇️ Download Extension (.zip)</button>
|
||||
|
||||
<div class="section-title" style="font-size:.8rem;margin-bottom:1rem">Installation Steps</div>
|
||||
<div class="ext-steps">
|
||||
<div class="ext-step">
|
||||
<div class="ext-step-num">1</div>
|
||||
<div class="ext-step-body">
|
||||
<strong>Unzip the downloaded file</strong>
|
||||
<p>Extract <code>dragon-wind-extension.zip</code> to a permanent folder on your computer — Chrome needs this folder to stay in place.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ext-step">
|
||||
<div class="ext-step-num">2</div>
|
||||
<div class="ext-step-body">
|
||||
<strong>Open Chrome Extensions</strong>
|
||||
<p>In Chrome, navigate to <code>chrome://extensions</code> or open the menu → More Tools → Extensions.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ext-step">
|
||||
<div class="ext-step-num">3</div>
|
||||
<div class="ext-step-body">
|
||||
<strong>Enable Developer Mode</strong>
|
||||
<p>Toggle the <strong>Developer mode</strong> switch in the top-right corner of the Extensions page.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ext-step">
|
||||
<div class="ext-step-num">4</div>
|
||||
<div class="ext-step-body">
|
||||
<strong>Load the extension</strong>
|
||||
<p>Click <strong>Load unpacked</strong>, then navigate to and select the <code>dragon-wind-extension</code> folder you unzipped in step 1.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ext-step">
|
||||
<div class="ext-step-num">5</div>
|
||||
<div class="ext-step-body">
|
||||
<strong>Configure the extension</strong>
|
||||
<p>Click the 🐉 Dragon Wind icon in Chrome's toolbar. Enter this server's URL and log in with your credentials. UDP uploads will now be available from any page.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ext-note">
|
||||
<strong>📡 UDP Port Forwarding</strong><br>
|
||||
For uploads from outside your local network, make sure your UDP relay port is forwarded in your router. You can find your assigned UDP port in the <strong>.env</strong> file (<code>RELAY_UDP_PORT</code>) or check the UDP Relay tab above.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Users -->
|
||||
<div class="admin-panel" id="admin-users">
|
||||
<div class="section-title">User Management</div>
|
||||
|
|
@ -753,7 +815,7 @@ function switchPage(name) {
|
|||
|
||||
function switchAdminTab(name) {
|
||||
document.querySelectorAll('.admin-tab').forEach((t,i) => {
|
||||
t.classList.toggle('active', ['s3','relay','ampp','users','folders'][i] === name);
|
||||
t.classList.toggle('active', ['s3','relay','ampp','extension','users','folders'][i] === name);
|
||||
});
|
||||
document.querySelectorAll('.admin-panel').forEach(p => p.classList.toggle('active', p.id === `admin-${name}`));
|
||||
if (name === 'users') loadUsers();
|
||||
|
|
@ -761,6 +823,25 @@ function switchAdminTab(name) {
|
|||
if (name === 'ampp') loadAmppConfig();
|
||||
}
|
||||
|
||||
async function downloadExtension() {
|
||||
const btn = event.target;
|
||||
btn.disabled = true; btn.textContent = '⏳ Preparing download…';
|
||||
try {
|
||||
const res = await fetch('/api/extension/download', { headers: { 'Authorization': 'Bearer ' + authToken } });
|
||||
if (!res.ok) throw new Error(`Server error ${res.status}`);
|
||||
const blob = await res.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url; a.download = 'dragon-wind-extension.zip'; a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
btn.textContent = '✅ Downloaded!';
|
||||
setTimeout(() => { btn.disabled = false; btn.textContent = '⬇️ Download Extension (.zip)'; }, 3000);
|
||||
} catch(e) {
|
||||
btn.disabled = false; btn.textContent = '⬇️ Download Extension (.zip)';
|
||||
showToast('Download failed: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// UPLOAD MODE
|
||||
// ============================================================
|
||||
|
|
|
|||
16
server.js
16
server.js
|
|
@ -10,6 +10,7 @@ const { NodeHttpHandler } = require("@smithy/node-http-handler");
|
|||
const fs = require("fs");
|
||||
const https = require("https");
|
||||
const http = require("http");
|
||||
const archiver = require("archiver");
|
||||
|
||||
require("events").defaultMaxListeners = 50;
|
||||
|
||||
|
|
@ -640,6 +641,21 @@ app.get("/api/ampp/jobs/:jobId", requireAuth, async (req, res) => {
|
|||
} catch (err) { res.status(500).json({ success: false, error: err.message }); }
|
||||
});
|
||||
|
||||
// ==================== CHROME EXTENSION DOWNLOAD ====================
|
||||
app.get("/api/extension/download", requireAdmin, (req, res) => {
|
||||
const extDir = path.join(__dirname, "chrome-extension");
|
||||
if (!fs.existsSync(extDir)) {
|
||||
return res.status(404).json({ success: false, error: "chrome-extension directory not found" });
|
||||
}
|
||||
res.setHeader("Content-Type", "application/zip");
|
||||
res.setHeader("Content-Disposition", "attachment; filename=\"dragon-wind-extension.zip\"");
|
||||
const archive = archiver("zip", { zlib: { level: 6 } });
|
||||
archive.on("error", (err) => { console.error("[Extension ZIP]", err.message); res.end(); });
|
||||
archive.pipe(res);
|
||||
archive.directory(extDir, "dragon-wind-extension");
|
||||
archive.finalize();
|
||||
});
|
||||
|
||||
// ==================== HEALTH ====================
|
||||
app.get("/api/health", (req, res) => {
|
||||
res.json({
|
||||
|
|
|
|||
Loading…
Reference in a new issue