deploy: bundle the official Datarhei Restreamer UI
Replaces the placeholder Dragon Fork landing page at / with the real
React SPA — the same UI that ships in upstream's datarhei/restreamer
image. Operators get the full process management dashboard, log
viewer, restream config, and so on.
Implementation: a new Docker stage 'ui-builder' (node:21-alpine3.20)
clones datarhei/restreamer-ui at a pinned tag (v1.14.0), runs
'yarn install + yarn build' with PUBLIC_URL="./" so all asset
references are relative, and the runtime stage pulls /ui/build into
/core/static. The existing seed-data.sh script then copies it into
/core/data on first boot.
Stacking order in /core/static:
1. UI bundle from ui-builder — provides index.html, the SPA bundle
and assets, _player, _playersite, etc.
2. Dragon Fork deploy/static/* — currently only whep-player.html;
the placeholder index.html was removed so the UI's wins.
Pinned to v1.14.0 (the most recent tagged restreamer-ui release)
rather than 'main' for reproducible builds. Bumping the pin is a
one-line ARG override.
Image size: ~+25MB compressed (Restreamer UI bundle is ~3MB
gzipped, plus the build-stage layer overhead until pruned).
UI-side configuration: the SPA defaults to talking to the
same-origin /api endpoints, which is exactly what we want when
serving from Core. No '?address=' query string needed on the URL.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
45f39a9132
commit
26991ec463
2 changed files with 28 additions and 74 deletions
|
|
@ -27,6 +27,23 @@ COPY . .
|
|||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
|
||||
RUN make release && make import && make ffmigrate
|
||||
|
||||
# ---- ui-builder ----
|
||||
# Builds the official Datarhei Restreamer UI (React 18 + MUI). Pinned
|
||||
# to a specific tag so reproducible. PUBLIC_URL=./ makes asset
|
||||
# references relative — the bundle then works when served from / or
|
||||
# any subdirectory under Core's static-disk filesystem.
|
||||
#
|
||||
# Pulling from the public github mirror keeps the Forgejo runner's
|
||||
# network footprint small; no auth required for clone.
|
||||
FROM node:21-alpine3.20 AS ui-builder
|
||||
ARG RESTREAMER_UI_REF=v1.14.0
|
||||
RUN apk add --no-cache git
|
||||
WORKDIR /ui
|
||||
RUN git clone --depth=1 --branch ${RESTREAMER_UI_REF} \
|
||||
https://github.com/datarhei/restreamer-ui.git . \
|
||||
&& yarn install --frozen-lockfile --network-timeout 600000 \
|
||||
&& PUBLIC_URL="./" GENERATE_SOURCEMAP=false yarn build
|
||||
|
||||
# ---- runtime ----
|
||||
# Alpine with ffmpeg (Core shells out to it for every restream process).
|
||||
# Scratch isn't an option here because the process manager needs ffmpeg
|
||||
|
|
@ -47,11 +64,17 @@ COPY --from=builder /src/ffmigrate /core/bin/ffmigrate
|
|||
COPY --from=builder /src/mime.types /core/mime.types
|
||||
COPY --from=builder /src/run.sh /core/bin/run.sh
|
||||
|
||||
# Dragon Fork landing page + browser WHEP player. Seeded into
|
||||
# /core/data on first boot by /core/bin/seed-data.sh below; the seed
|
||||
# is a no-op when the operator has already put content in /core/data.
|
||||
COPY --from=builder /src/deploy/truenas/core/static/ /core/static/
|
||||
COPY --from=builder /src/deploy/truenas/core/seed-data.sh /core/bin/seed-data.sh
|
||||
# Static content for /core/data, seeded on first boot by seed-data.sh.
|
||||
# Stacking order:
|
||||
# 1. Restreamer UI bundle (the React SPA — gives us index.html)
|
||||
# 2. Dragon Fork extras (whep-player.html, etc.) — won't overwrite
|
||||
# the UI's index.html (seed-data is no-clobber).
|
||||
#
|
||||
# The result: GET / serves the official Restreamer dashboard, and
|
||||
# /whep-player.html serves the standalone WHEP smoke player.
|
||||
COPY --from=ui-builder /ui/build/ /core/static/
|
||||
COPY --from=builder /src/deploy/truenas/core/static/ /core/static/
|
||||
COPY --from=builder /src/deploy/truenas/core/seed-data.sh /core/bin/seed-data.sh
|
||||
|
||||
RUN chmod +x /core/bin/seed-data.sh && mkdir -p /core/config /core/data
|
||||
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Datarhei — Dragon Fork</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root { color-scheme: dark; --fg:#e7e7ea; --bg:#0d0e12; --accent:#ff6633; --muted:#8b8e98; --panel:#1a1c22; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin:0; font:15px/1.5 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; background:var(--bg); color:var(--fg); min-height:100vh; }
|
||||
header { padding:1.5rem 2rem; border-bottom:1px solid #232530; }
|
||||
header h1 { margin:0; font-size:1.2rem; letter-spacing:0.02em; }
|
||||
header h1 .accent { color:var(--accent); }
|
||||
header .subtitle { color:var(--muted); font-size:0.85rem; margin-top:0.3rem; }
|
||||
main { max-width:840px; margin:0 auto; padding:2rem; }
|
||||
h2 { font-size:1.05rem; margin-top:2rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.06em; }
|
||||
.card { background:var(--panel); border-radius:10px; padding:1.25rem 1.5rem; margin-bottom:1rem; }
|
||||
.card a { color:var(--accent); text-decoration:none; font-weight:600; }
|
||||
.card a:hover { text-decoration:underline; }
|
||||
.card p { color:var(--muted); margin:0.4rem 0 0; font-size:0.9rem; }
|
||||
code { background:#0d0e12; padding:0.1rem 0.4rem; border-radius:4px; font:0.85em ui-monospace,Menlo,Consolas,monospace; }
|
||||
footer { padding:2rem; text-align:center; color:var(--muted); font-size:0.8rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Datarhei <span class="accent">Dragon Fork</span></h1>
|
||||
<div class="subtitle">a fork of <a href="https://github.com/datarhei/core" style="color:var(--muted)">datarhei/core</a> with native WebRTC (WHEP) egress</div>
|
||||
</header>
|
||||
<main>
|
||||
<h2>Quick links</h2>
|
||||
<div class="card">
|
||||
<a href="/api/swagger/index.html">API documentation (Swagger UI)</a>
|
||||
<p>Full HTTP API including <code>/api/v3/process</code>, <code>/api/v3/whep/{id}</code>, RTMP / SRT / config / metrics. Most endpoints require a JWT — issue one via <code>POST /api/login</code>.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<a href="/whep-player.html">Browser WHEP player</a>
|
||||
<p>Self-contained subscriber for any process whose <code>config.webrtc.enabled = true</code>. Paste the WHEP URL and a JWT; press Subscribe.</p>
|
||||
</div>
|
||||
|
||||
<h2>About this build</h2>
|
||||
<div class="card" id="about">Loading…</div>
|
||||
|
||||
<h2>How to add a WebRTC stream</h2>
|
||||
<div class="card">
|
||||
<p style="color:var(--fg)">Create a process with <code>"webrtc": { "enabled": true }</code>. Once it starts, <code>POST /api/v3/whep/<process-id></code> takes an SDP offer and returns an SDP answer.</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>Datarhei — Dragon Fork · Apache License 2.0 · Built on datarhei Core + Pion WebRTC</footer>
|
||||
<script>
|
||||
fetch('/api')
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
const el = document.getElementById('about');
|
||||
const v = d.version || {};
|
||||
el.innerHTML = '<p style="color:var(--fg)">' +
|
||||
'<strong>' + (d.fork || d.app) + '</strong> · ' +
|
||||
'instance <code>' + (d.name || '?') + '</code> · ' +
|
||||
'version <code>' + (v.number || '?') + '</code> · ' +
|
||||
'commit <code>' + ((v.repository_commit || '?').slice(0,8)) + '</code>' +
|
||||
'</p>';
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById('about').innerHTML =
|
||||
'<p>Status panel needs an authenticated request. Visit <a href="/api/swagger/index.html">Swagger</a> to log in.</p>';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue