Add Synology media-node install script for multi-NAS enrollments.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0611cb5f8c
commit
a26d1a4c45
6 changed files with 384 additions and 22 deletions
|
|
@ -48,6 +48,7 @@ export default function NodesPage() {
|
||||||
const [nodes, setNodes] = useState<Node[]>([]);
|
const [nodes, setNodes] = useState<Node[]>([]);
|
||||||
const [shelves, setShelves] = useState<Shelf[]>([]);
|
const [shelves, setShelves] = useState<Shelf[]>([]);
|
||||||
const [showEnroll, setShowEnroll] = useState(false);
|
const [showEnroll, setShowEnroll] = useState(false);
|
||||||
|
const [enrollPlatform, setEnrollPlatform] = useState<"unraid" | "synology">("unraid");
|
||||||
const [enrollName, setEnrollName] = useState("");
|
const [enrollName, setEnrollName] = useState("");
|
||||||
const [enrollLocation, setEnrollLocation] = useState("");
|
const [enrollLocation, setEnrollLocation] = useState("");
|
||||||
const [enrollToken, setEnrollToken] = useState("");
|
const [enrollToken, setEnrollToken] = useState("");
|
||||||
|
|
@ -94,15 +95,39 @@ export default function NodesPage() {
|
||||||
setEnrollToken(data.token);
|
setEnrollToken(data.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setPlatform(platform: "unraid" | "synology") {
|
||||||
|
setEnrollPlatform(platform);
|
||||||
|
if (platform === "synology") {
|
||||||
|
setMoviesPath((p) => (p.startsWith("/mnt/") || !p ? "/volume1/Media/Films/Films" : p));
|
||||||
|
setSeriesPath((p) => (p.startsWith("/mnt/") || !p ? "/volume1/Media/Series/Series" : p));
|
||||||
|
if (!enrollName || enrollName.startsWith("Unraid")) {
|
||||||
|
setEnrollName("Synology-Node-01");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setMoviesPath((p) => (p.startsWith("/volume") || !p ? "/mnt/user/movies" : p));
|
||||||
|
setSeriesPath((p) => (p.startsWith("/volume") || !p ? "/mnt/user/series" : p));
|
||||||
|
if (!enrollName || enrollName.startsWith("Synology")) {
|
||||||
|
setEnrollName("Unraid-Node-01");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function installCommand() {
|
function installCommand() {
|
||||||
|
const script = enrollPlatform === "synology" ? "synology.sh" : "unraid.sh";
|
||||||
|
const defaultName =
|
||||||
|
enrollPlatform === "synology" ? "Synology-Node-01" : "Unraid-Node-01";
|
||||||
const series = seriesPath.trim()
|
const series = seriesPath.trim()
|
||||||
? ` \\\n --series='${seriesPath.trim()}'`
|
? ` \\\n --series='${seriesPath.trim()}'`
|
||||||
: "";
|
: "";
|
||||||
return `curl -fsSL ${MASTER_URL}/install/unraid.sh | bash -s -- \\
|
const mediaRoot =
|
||||||
|
enrollPlatform === "synology"
|
||||||
|
? ` \\\n --media-root='/volume1'`
|
||||||
|
: "";
|
||||||
|
return `curl -fsSL ${MASTER_URL}/install/${script} | bash -s -- \\
|
||||||
--token='${enrollToken}' \\
|
--token='${enrollToken}' \\
|
||||||
--name='${enrollName || "Unraid-Node-01"}' \\
|
--name='${enrollName || defaultName}' \\
|
||||||
--public-url='${publicUrl.trim()}' \\
|
--public-url='${publicUrl.trim()}' \\
|
||||||
--movies='${moviesPath.trim()}'${series}`;
|
--movies='${moviesPath.trim()}'${series}${mediaRoot}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyInstall() {
|
async function copyInstall() {
|
||||||
|
|
@ -243,15 +268,53 @@ export default function NodesPage() {
|
||||||
{showEnroll && (
|
{showEnroll && (
|
||||||
<div className="card" style={{ marginBottom: "2rem" }}>
|
<div className="card" style={{ marginBottom: "2rem" }}>
|
||||||
<h2 style={{ fontSize: "1rem", color: "#fafafa", textTransform: "none", marginBottom: "1rem" }}>
|
<h2 style={{ fontSize: "1rem", color: "#fafafa", textTransform: "none", marginBottom: "1rem" }}>
|
||||||
Unraid-node toevoegen
|
Node toevoegen
|
||||||
</h2>
|
</h2>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Platform</label>
|
||||||
|
<div style={{ display: "flex", gap: "0.5rem" }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setPlatform("unraid")}
|
||||||
|
style={{
|
||||||
|
opacity: enrollPlatform === "unraid" ? 1 : 0.55,
|
||||||
|
outline: enrollPlatform === "unraid" ? "2px solid #71717a" : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Unraid
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setPlatform("synology")}
|
||||||
|
style={{
|
||||||
|
opacity: enrollPlatform === "synology" ? 1 : 0.55,
|
||||||
|
outline: enrollPlatform === "synology" ? "2px solid #71717a" : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Synology
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<p style={{ color: "#a1a1aa", marginBottom: "1rem", fontSize: "0.9rem" }}>
|
<p style={{ color: "#a1a1aa", marginBottom: "1rem", fontSize: "0.9rem" }}>
|
||||||
Eenmalig installeren met brede <code>/mnt/user</code>-mount. Daarna: planken in Instellingen,
|
{enrollPlatform === "synology" ? (
|
||||||
scanpaden + plankkeuze hier onder Edit.
|
<>
|
||||||
|
SSH + Container Manager. Brede mount <code>/volume1</code> — daarna scanpaden via Edit.
|
||||||
|
Meerdere NAS'en: eigen naam + publieke URL per box.
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Eenmalig installeren met brede <code>/mnt/user</code>-mount. Daarna: planken in Instellingen,
|
||||||
|
scanpaden + plankkeuze hier onder Edit.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Naam</label>
|
<label>Naam</label>
|
||||||
<input value={enrollName} onChange={(e) => setEnrollName(e.target.value)} placeholder="Unraid-Node-01" />
|
<input
|
||||||
|
value={enrollName}
|
||||||
|
onChange={(e) => setEnrollName(e.target.value)}
|
||||||
|
placeholder={enrollPlatform === "synology" ? "Synology-Node-01" : "Unraid-Node-01"}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Locatie</label>
|
<label>Locatie</label>
|
||||||
|
|
@ -263,18 +326,30 @@ export default function NodesPage() {
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Eerste films-pad (bootstrap)</label>
|
<label>Eerste films-pad (bootstrap)</label>
|
||||||
<input value={moviesPath} onChange={(e) => setMoviesPath(e.target.value)} placeholder="/mnt/user/movies" />
|
<input
|
||||||
|
value={moviesPath}
|
||||||
|
onChange={(e) => setMoviesPath(e.target.value)}
|
||||||
|
placeholder={
|
||||||
|
enrollPlatform === "synology" ? "/volume1/Media/Films/Films" : "/mnt/user/movies"
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Eerste series-pad (optioneel)</label>
|
<label>Eerste series-pad (optioneel)</label>
|
||||||
<input value={seriesPath} onChange={(e) => setSeriesPath(e.target.value)} placeholder="/mnt/user/series" />
|
<input
|
||||||
|
value={seriesPath}
|
||||||
|
onChange={(e) => setSeriesPath(e.target.value)}
|
||||||
|
placeholder={
|
||||||
|
enrollPlatform === "synology" ? "/volume1/Media/Series/Series" : "/mnt/user/series"
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={createEnrollmentToken}>Token genereren</button>
|
<button onClick={createEnrollmentToken}>Token genereren</button>
|
||||||
{enrollToken && (
|
{enrollToken && (
|
||||||
<>
|
<>
|
||||||
<div className="token-display" style={{ marginTop: "1rem" }}>{enrollToken}</div>
|
<div className="token-display" style={{ marginTop: "1rem" }}>{enrollToken}</div>
|
||||||
<h2 style={{ fontSize: "0.95rem", color: "#fafafa", textTransform: "none", margin: "1.25rem 0 0.5rem" }}>
|
<h2 style={{ fontSize: "0.95rem", color: "#fafafa", textTransform: "none", margin: "1.25rem 0 0.5rem" }}>
|
||||||
Unraid-terminal
|
{enrollPlatform === "synology" ? "Synology SSH" : "Unraid-terminal"}
|
||||||
</h2>
|
</h2>
|
||||||
<pre className="token-display" style={{ whiteSpace: "pre-wrap", fontSize: "0.8rem" }}>
|
<pre className="token-display" style={{ whiteSpace: "pre-wrap", fontSize: "0.8rem" }}>
|
||||||
{installCommand()}
|
{installCommand()}
|
||||||
|
|
|
||||||
|
|
@ -10,24 +10,45 @@ function installDir(): string {
|
||||||
"/app/install",
|
"/app/install",
|
||||||
];
|
];
|
||||||
for (const dir of candidates) {
|
for (const dir of candidates) {
|
||||||
if (fs.existsSync(path.join(dir, "unraid.sh"))) return dir;
|
if (
|
||||||
|
fs.existsSync(path.join(dir, "unraid.sh")) ||
|
||||||
|
fs.existsSync(path.join(dir, "synology.sh"))
|
||||||
|
) {
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return candidates[0];
|
return candidates[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendInstallScript(
|
||||||
|
reply: { header: (k: string, v: string) => unknown; status: (c: number) => { send: (b: string) => unknown }; send: (b: string) => unknown },
|
||||||
|
dir: string,
|
||||||
|
filename: string,
|
||||||
|
publicUrl: string
|
||||||
|
) {
|
||||||
|
const file = path.join(dir, filename);
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
return reply
|
||||||
|
.status(404)
|
||||||
|
.send("Install script not found — redeploy Master with install assets.");
|
||||||
|
}
|
||||||
|
let script = fs.readFileSync(file, "utf8");
|
||||||
|
script = script.replaceAll("__MASTER_PUBLIC_URL__", publicUrl.replace(/\/$/, ""));
|
||||||
|
reply.header("Content-Type", "text/x-shellscript; charset=utf-8");
|
||||||
|
reply.header("Content-Disposition", `inline; filename="${filename}"`);
|
||||||
|
return reply.send(script);
|
||||||
|
}
|
||||||
|
|
||||||
export async function registerInstallRoutes(app: FastifyInstance, config: Config) {
|
export async function registerInstallRoutes(app: FastifyInstance, config: Config) {
|
||||||
const dir = installDir();
|
const dir = installDir();
|
||||||
|
const publicUrl = config.PUBLIC_URL;
|
||||||
|
|
||||||
app.get("/install/unraid.sh", async (_request, reply) => {
|
app.get("/install/unraid.sh", async (_request, reply) => {
|
||||||
const file = path.join(dir, "unraid.sh");
|
return sendInstallScript(reply, dir, "unraid.sh", publicUrl);
|
||||||
if (!fs.existsSync(file)) {
|
});
|
||||||
return reply.status(404).send("Install script not found — redeploy Master with install assets.");
|
|
||||||
}
|
app.get("/install/synology.sh", async (_request, reply) => {
|
||||||
let script = fs.readFileSync(file, "utf8");
|
return sendInstallScript(reply, dir, "synology.sh", publicUrl);
|
||||||
script = script.replaceAll("__MASTER_PUBLIC_URL__", config.PUBLIC_URL.replace(/\/$/, ""));
|
|
||||||
reply.header("Content-Type", "text/x-shellscript; charset=utf-8");
|
|
||||||
reply.header("Content-Disposition", 'inline; filename="unraid.sh"');
|
|
||||||
return reply.send(script);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const bin of ["media-node-linux-amd64", "media-node-linux-arm64"] as const) {
|
for (const bin of ["media-node-linux-amd64", "media-node-linux-arm64"] as const) {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ COPY --from=builder /out/local-packages ./local-packages
|
||||||
COPY --from=node-build /media-node-linux-amd64 /app/install/media-node-linux-amd64
|
COPY --from=node-build /media-node-linux-amd64 /app/install/media-node-linux-amd64
|
||||||
COPY --from=node-build /media-node-linux-arm64 /app/install/media-node-linux-arm64
|
COPY --from=node-build /media-node-linux-arm64 /app/install/media-node-linux-arm64
|
||||||
COPY deploy/node/install-unraid.sh /app/install/unraid.sh
|
COPY deploy/node/install-unraid.sh /app/install/unraid.sh
|
||||||
|
COPY deploy/node/install-synology.sh /app/install/synology.sh
|
||||||
|
|
||||||
# Remove workspace protocol deps before npm install
|
# Remove workspace protocol deps before npm install
|
||||||
RUN node -e "\
|
RUN node -e "\
|
||||||
|
|
|
||||||
222
deploy/node/install-synology.sh
Normal file
222
deploy/node/install-synology.sh
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Synology media-node installer — geen image build, alleen download + docker run
|
||||||
|
# Werkt op DSM 7 + Container Manager (SSH). Meerdere NAS'en: unieke --name + --public-url.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MASTER_URL="${MASTER_URL:-__MASTER_PUBLIC_URL__}"
|
||||||
|
TOKEN=""
|
||||||
|
NAME=""
|
||||||
|
PUBLIC_URL=""
|
||||||
|
MOVIES=""
|
||||||
|
SERIES=""
|
||||||
|
PORT="${PORT:-8080}"
|
||||||
|
APPDATA="${APPDATA:-/volume1/docker/media-node}"
|
||||||
|
MEDIA_ROOT="${MEDIA_ROOT:-/volume1}"
|
||||||
|
PUID="${PUID:-1026}"
|
||||||
|
PGID="${PGID:-100}"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Gebruik (SSH als admin/root op Synology):
|
||||||
|
curl -fsSL ${MASTER_URL}/install/synology.sh | bash -s -- \\
|
||||||
|
--token=TOKEN --name=Synology-01 \\
|
||||||
|
--public-url=https://node02.example.com \\
|
||||||
|
--movies=/volume1/Media/Films/Films \\
|
||||||
|
[--series=/volume1/Media/Series/Series] \\
|
||||||
|
[--media-root=/volume1]
|
||||||
|
|
||||||
|
Opties:
|
||||||
|
--token= Enrollment token uit Admin → Nodes
|
||||||
|
--name= Node-naam (uniek per NAS)
|
||||||
|
--public-url= Publieke HTTPS-URL via NPM (poort ${PORT})
|
||||||
|
--movies= Synology-pad naar films — bootstrap; later via Admin uitbreiden
|
||||||
|
--series= Synology-pad naar series (optioneel)
|
||||||
|
--media-root= Host-root die gemount wordt (default /volume1) zodat nieuwe
|
||||||
|
mappen vanaf Admin zichtbaar zijn zonder herinstall
|
||||||
|
--port= Host-poort (default 8080; bij meerdere nodes op 1 LAN: uniek)
|
||||||
|
--appdata= Appdata-map (default /volume1/docker/media-node)
|
||||||
|
--uid= Container-user (default 1026 = typische DSM-admin)
|
||||||
|
--gid= Container-group (default 100 = users)
|
||||||
|
--master= Master-URL (default: ${MASTER_URL})
|
||||||
|
|
||||||
|
Tip: uid/gid van een share-owner: id JOUW_DSM_USER
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--token=*) TOKEN="${arg#*=}" ;;
|
||||||
|
--name=*) NAME="${arg#*=}" ;;
|
||||||
|
--public-url=*) PUBLIC_URL="${arg#*=}" ;;
|
||||||
|
--movies=*) MOVIES="${arg#*=}" ;;
|
||||||
|
--series=*) SERIES="${arg#*=}" ;;
|
||||||
|
--media-root=*) MEDIA_ROOT="${arg#*=}" ;;
|
||||||
|
--port=*) PORT="${arg#*=}" ;;
|
||||||
|
--appdata=*) APPDATA="${arg#*=}" ;;
|
||||||
|
--uid=*) PUID="${arg#*=}" ;;
|
||||||
|
--gid=*) PGID="${arg#*=}" ;;
|
||||||
|
--master=*) MASTER_URL="${arg#*=}" ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "Onbekende optie: $arg"; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
docker_cmd() {
|
||||||
|
if docker info >/dev/null 2>&1; then
|
||||||
|
docker "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if command -v sudo >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then
|
||||||
|
sudo docker "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "Docker niet gevonden. Op Synology: Container Manager installeren + SSH inschakelen."
|
||||||
|
echo "Draai dit script als root of een user in de docker-groep."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt() {
|
||||||
|
local label="$1" var="$2" current="$3" flag="$4"
|
||||||
|
if [ -n "$current" ]; then
|
||||||
|
eval "$var=\"\$current\""
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ -t 0 ]; then
|
||||||
|
local val=""
|
||||||
|
printf "%s" "$label"
|
||||||
|
read -r val
|
||||||
|
eval "$var=\"\$val\""
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "Ontbrekende optie: $flag (bij curl|bash alle flags meegeven)."
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "=== Media Node — Synology install ==="
|
||||||
|
echo "Master: $MASTER_URL"
|
||||||
|
echo
|
||||||
|
|
||||||
|
prompt "Enrollment token: " TOKEN "$TOKEN" "--token"
|
||||||
|
prompt "Node-naam [Synology-Node-01]: " NAME "${NAME}" "--name"
|
||||||
|
if [ -z "$NAME" ]; then NAME="Synology-Node-01"; fi
|
||||||
|
prompt "Publieke URL (https://node02....): " PUBLIC_URL "$PUBLIC_URL" "--public-url"
|
||||||
|
prompt "Films-pad [/volume1/Media/Films/Films]: " MOVIES "${MOVIES}" "--movies"
|
||||||
|
if [ -z "$MOVIES" ] && [ -t 0 ]; then MOVIES="/volume1/Media/Films/Films"; fi
|
||||||
|
if [ -z "$MOVIES" ]; then
|
||||||
|
echo "Ontbrekende optie: --movies"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -t 0 ] && [ -z "$SERIES" ]; then
|
||||||
|
printf "Series-pad (leeg = overslaan): "
|
||||||
|
read -r SERIES || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOKEN="$(echo -n "$TOKEN" | tr -d '\r\n')"
|
||||||
|
NAME="$(echo -n "$NAME" | tr -d '\r\n')"
|
||||||
|
PUBLIC_URL="$(echo -n "$PUBLIC_URL" | sed 's:/*$::')"
|
||||||
|
MOVIES="$(echo -n "$MOVIES" | tr -d '\r\n')"
|
||||||
|
SERIES="$(echo -n "$SERIES" | tr -d '\r\n')"
|
||||||
|
MEDIA_ROOT="$(echo -n "$MEDIA_ROOT" | sed 's:/*$::')"
|
||||||
|
MASTER_URL="$(echo -n "$MASTER_URL" | sed 's:/*$::')"
|
||||||
|
APPDATA="$(echo -n "$APPDATA" | sed 's:/*$::')"
|
||||||
|
|
||||||
|
if [ -z "$TOKEN" ] || [ -z "$NAME" ] || [ -z "$PUBLIC_URL" ] || [ -z "$MOVIES" ]; then
|
||||||
|
echo "Token, naam, public-url en movies zijn verplicht."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$MEDIA_ROOT" ]; then
|
||||||
|
echo "Fout: media-root bestaat niet: $MEDIA_ROOT"
|
||||||
|
echo "Gebruik bijv. --media-root=/volume1 of /volume2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -d "$MOVIES" ]; then
|
||||||
|
echo "Waarschuwing: films-pad bestaat niet: $MOVIES"
|
||||||
|
fi
|
||||||
|
if [ -n "$SERIES" ] && [ ! -d "$SERIES" ]; then
|
||||||
|
echo "Waarschuwing: series-pad bestaat niet: $SERIES"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$MOVIES" in
|
||||||
|
"$MEDIA_ROOT"|"$MEDIA_ROOT"/*) ;;
|
||||||
|
*) echo "Waarschuwing: --movies ligt niet onder --media-root ($MEDIA_ROOT). Admin-paden moeten onder die root vallen." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64|amd64) BIN_NAME="media-node-linux-amd64" ;;
|
||||||
|
aarch64|arm64) BIN_NAME="media-node-linux-arm64" ;;
|
||||||
|
*) echo "Niet-ondersteunde architectuur: $ARCH"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mkdir -p "$APPDATA/config/credentials" "$APPDATA/data" "$APPDATA/bin"
|
||||||
|
chown -R "${PUID}:${PGID}" "$APPDATA" 2>/dev/null || true
|
||||||
|
|
||||||
|
BIN_PATH="$APPDATA/bin/media-node"
|
||||||
|
echo "Binary downloaden ($BIN_NAME)..."
|
||||||
|
curl -fsSL "$MASTER_URL/install/$BIN_NAME" -o "$BIN_PATH"
|
||||||
|
chmod +x "$BIN_PATH"
|
||||||
|
chown "${PUID}:${PGID}" "$BIN_PATH" 2>/dev/null || true
|
||||||
|
|
||||||
|
docker_cmd rm -f media-node >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
VOLUMES=(
|
||||||
|
-v "$APPDATA/config:/etc/media-node"
|
||||||
|
-v "$APPDATA/data:/var/lib/media-node"
|
||||||
|
-v "$BIN_PATH:/usr/local/bin/media-node:ro"
|
||||||
|
-v "$MEDIA_ROOT:$MEDIA_ROOT:ro"
|
||||||
|
)
|
||||||
|
if [ -d /etc/ssl/certs ]; then
|
||||||
|
VOLUMES+=(-v /etc/ssl/certs:/etc/ssl/certs:ro)
|
||||||
|
fi
|
||||||
|
# Admin → Herstart via docker.sock (DSM: vaak root:root — group-add helpt niet altijd)
|
||||||
|
if [ -S /var/run/docker.sock ]; then
|
||||||
|
VOLUMES+=(-v /var/run/docker.sock:/var/run/docker.sock)
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENV_SERIES=""
|
||||||
|
if [ -n "$SERIES" ]; then
|
||||||
|
ENV_SERIES="$SERIES"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOCKER_GID=""
|
||||||
|
if [ -S /var/run/docker.sock ]; then
|
||||||
|
DOCKER_GID="$(stat -c '%g' /var/run/docker.sock 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
GROUP_ARGS=()
|
||||||
|
if [ -n "$DOCKER_GID" ] && [ "$DOCKER_GID" != "0" ]; then
|
||||||
|
GROUP_ARGS+=(--group-add "$DOCKER_GID")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Container starten (media-root=$MEDIA_ROOT, user=${PUID}:${PGID})..."
|
||||||
|
docker_cmd run -d \
|
||||||
|
--name media-node \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p "${PORT}:8080" \
|
||||||
|
-e "MEDIA_NODE_NAME=$NAME" \
|
||||||
|
-e "MEDIA_NODE_MASTER_URL=$MASTER_URL" \
|
||||||
|
-e "MEDIA_NODE_PUBLIC_URL=$PUBLIC_URL" \
|
||||||
|
-e "MEDIA_NODE_MOVIES=$MOVIES" \
|
||||||
|
-e "MEDIA_NODE_SERIES=$ENV_SERIES" \
|
||||||
|
-e "MEDIA_NODE_ENROLLMENT_TOKEN=$TOKEN" \
|
||||||
|
-e "MEDIA_NODE_FULL_SCAN_INTERVAL=off" \
|
||||||
|
-e "MEDIA_NODE_FULL_SCAN_AT=03:30" \
|
||||||
|
-e "MEDIA_NODE_CONTAINER_NAME=media-node" \
|
||||||
|
-e "TZ=${TZ:-Europe/Amsterdam}" \
|
||||||
|
--user "${PUID}:${PGID}" \
|
||||||
|
"${GROUP_ARGS[@]}" \
|
||||||
|
"${VOLUMES[@]}" \
|
||||||
|
--entrypoint /usr/local/bin/media-node \
|
||||||
|
alpine:3.20 \
|
||||||
|
run
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Klaar. Logs: docker logs -f media-node"
|
||||||
|
echo "Status: in Admin → Nodes (moet online worden)"
|
||||||
|
echo "NPM: $PUBLIC_URL → Synology-LAN-IP:$PORT (buffering uit, Force SSL)"
|
||||||
|
echo "Paden: Admin → Nodes → Edit (host-paden onder $MEDIA_ROOT)"
|
||||||
|
echo "UID/GID: bij permission denied: --uid=/--gid= van 'id jouwuser'"
|
||||||
|
echo
|
||||||
|
echo "Na succes: credentials staan in $APPDATA — herstart zonder nieuwe token is ok."
|
||||||
|
echo "Meerdere NAS'en: elk een eigen --name, --public-url en (indien nodig) --port."
|
||||||
|
|
@ -139,8 +139,8 @@ Browsers roepen alleen `/api/...` op het admin-domein aan — dat is correct.
|
||||||
## Na succesvolle deploy
|
## Na succesvolle deploy
|
||||||
|
|
||||||
1. Admin UI → **Instellingen** → Stremio addon token genereren
|
1. Admin UI → **Instellingen** → Stremio addon token genereren
|
||||||
2. Admin UI → **Nodes** → token + **Kopieer install-commando** (Unraid: één curl|bash)
|
2. Admin UI → **Nodes** → platform Unraid/Synology → token + **Kopieer install-commando**
|
||||||
3. Op Unraid: zie [unraid-node.md](./unraid-node.md). Op Linux: `sudo media-node install` met
|
3. Op Unraid: zie [unraid-node.md](./unraid-node.md). Op Synology: [synology-node.md](./synology-node.md). Op Linux: `sudo media-node install` met
|
||||||
Master URL = `https://master.jouwdomein.nl`
|
Master URL = `https://master.jouwdomein.nl`
|
||||||
|
|
||||||
## Alternatief: Dokploy + jouw NPM
|
## Alternatief: Dokploy + jouw NPM
|
||||||
|
|
|
||||||
43
docs/synology-node.md
Normal file
43
docs/synology-node.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Media Node op Synology (DSM 7)
|
||||||
|
|
||||||
|
Zelfde model als Unraid: één curl-commando, geen image build.
|
||||||
|
|
||||||
|
## Voorwaarden
|
||||||
|
|
||||||
|
1. **Container Manager** geïnstalleerd
|
||||||
|
2. **SSH** aan (Configuratiescherm → Terminal & SNMP)
|
||||||
|
3. **NPM**: Proxy Host `nodeXX.…` → Synology LAN-IP `:8080`, Force SSL, buffering uit
|
||||||
|
|
||||||
|
## Installeren
|
||||||
|
|
||||||
|
1. Admin → Nodes → **Node toevoegen** → kies platform **Synology** → token + paden → kopieer commando
|
||||||
|
2. SSH naar de NAS (admin/root) en plak het commando
|
||||||
|
|
||||||
|
Of handmatig:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://master.vonas.nl/install/synology.sh | bash -s -- \
|
||||||
|
--token='ENR-...' \
|
||||||
|
--name='Synology-Node-01' \
|
||||||
|
--public-url='https://node2.example.com' \
|
||||||
|
--movies='/volume1/Media/Films/Films' \
|
||||||
|
--series='/volume1/Media/Series/Series' \
|
||||||
|
--media-root='/volume1'
|
||||||
|
```
|
||||||
|
|
||||||
|
Defaults: appdata `/volume1/docker/media-node`, user `1026:100`.
|
||||||
|
|
||||||
|
Bij **permission denied** op media:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
id jouw_dsm_user
|
||||||
|
# daarna opnieuw met --uid=... --gid=...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Meerdere Synology’s
|
||||||
|
|
||||||
|
Elke NAS: eigen `--name`, eigen `--public-url` (NPM), eventueel andere `--port` als ze hetzelfde LAN delen achter één router zonder aparte hosts.
|
||||||
|
|
||||||
|
Paden in Admin zijn altijd de **echte Synology-paden** onder `--media-root` (bijv. `/volume1/...`).
|
||||||
|
|
||||||
|
Controle: `docker logs -f media-node` → Admin → Nodes **online**.
|
||||||
Loading…
Reference in a new issue