105 lines
4.0 KiB
Bash
105 lines
4.0 KiB
Bash
#!/bin/sh
|
|
set -eu
|
|
|
|
cd /usr/local/indiekit
|
|
NODE_BIN="${NODE_BIN:-/usr/local/bin/node}"
|
|
|
|
# Optional: load environment from local .env file
|
|
# (dotenv syntax, supports spaces in values).
|
|
if [ -f .env ]; then
|
|
eval "$("${NODE_BIN}" -e '
|
|
const fs = require("node:fs");
|
|
const dotenv = require("dotenv");
|
|
const parsed = dotenv.parse(fs.readFileSync(".env"));
|
|
for (const [key, value] of Object.entries(parsed)) {
|
|
const safe = String(value).split("\x27").join("\x27\"\x27\"\x27");
|
|
process.stdout.write(`export ${key}=\x27${safe}\x27\n`);
|
|
}
|
|
')"
|
|
fi
|
|
|
|
: "${SECRET:?SECRET is required}"
|
|
if [ "${INDIEKIT_ALLOW_PASSWORD_SETUP:-0}" != "1" ]; then
|
|
: "${PASSWORD_SECRET:?PASSWORD_SECRET is required}"
|
|
fi
|
|
|
|
# Allow either full Mongo URL or decomposed credentials.
|
|
if [ -z "${MONGO_URL:-}" ]; then
|
|
: "${MONGO_USERNAME:?MONGO_USERNAME is required when MONGO_URL is not set}"
|
|
: "${MONGO_PASSWORD:?MONGO_PASSWORD is required when MONGO_URL is not set}"
|
|
export MONGO_AUTH_SOURCE="${MONGO_AUTH_SOURCE:-admin}"
|
|
fi
|
|
|
|
# Force production runtime and keep debug logging disabled.
|
|
export NODE_ENV="production"
|
|
export INDIEKIT_DEBUG="0"
|
|
unset DEBUG
|
|
|
|
# Verify production auth/session hardening before launching server.
|
|
"${NODE_BIN}" scripts/preflight-production-security.mjs
|
|
|
|
# Verify MongoDB credentials/connectivity before launching server.
|
|
"${NODE_BIN}" scripts/preflight-mongo-connection.mjs
|
|
|
|
# Ensure ActivityPub has an RSA keypair for HTTP Signature delivery.
|
|
"${NODE_BIN}" scripts/preflight-activitypub-rsa-key.mjs
|
|
|
|
# Normalize ActivityPub profile URL fields (icon/image/aliases) in MongoDB.
|
|
"${NODE_BIN}" scripts/preflight-activitypub-profile-urls.mjs
|
|
|
|
for patch in scripts/patch-*.mjs; do
|
|
echo "[startup] Applying patch: $patch"
|
|
"${NODE_BIN}" "$patch"
|
|
done
|
|
|
|
"${NODE_BIN}" --require ./metrics-shim.cjs node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs &
|
|
INDIEKIT_PID="$!"
|
|
|
|
# Webmention sender — polls every N seconds (see @rmdes/indiekit-endpoint-webmention-sender README)
|
|
# Connects directly to Indiekit (not through nginx) so the Host header doesn't
|
|
# need to match any nginx server_name. nginx port 80 returns 444 for unknown hosts.
|
|
WEBMENTION_POLL_INTERVAL="${WEBMENTION_SENDER_POLL_INTERVAL:-300}"
|
|
INDIEKIT_DIRECT_URL="http://${INDIEKIT_BIND_HOST:-127.0.0.1}:${PORT:-3000}"
|
|
WEBMENTION_ENDPOINT="${INDIEKIT_DIRECT_URL}${WEBMENTION_SENDER_MOUNT_PATH:-/webmention-sender}"
|
|
WEBMENTION_ORIGIN="${PUBLICATION_URL:-${SITE_URL:-}}"
|
|
|
|
(
|
|
echo "[webmention] Starting auto-send polling every ${WEBMENTION_POLL_INTERVAL}s (${WEBMENTION_ENDPOINT})"
|
|
# Wait for the webmention-sender endpoint itself to be ready (up to 3 minutes).
|
|
# Using the plugin's own /api/status ensures MongoDB collections and plugin
|
|
# routes are fully initialised, not just the bare Express server.
|
|
_i=0
|
|
until curl -sf "${WEBMENTION_ENDPOINT}/api/status" -o /dev/null 2>&1; do
|
|
_i=$((_i + 1))
|
|
[ $_i -lt 90 ] || { echo "[webmention] Warning: webmention-sender not ready after 180s, proceeding anyway"; break; }
|
|
sleep 2
|
|
done
|
|
echo "[webmention] Webmention sender ready"
|
|
while true; do
|
|
TOKEN="$(
|
|
WEBMENTION_ORIGIN="$WEBMENTION_ORIGIN" WEBMENTION_SECRET="$SECRET" \
|
|
"${NODE_BIN}" -e '
|
|
const jwt = require("jsonwebtoken");
|
|
const me = process.env.WEBMENTION_ORIGIN;
|
|
const secret = process.env.WEBMENTION_SECRET;
|
|
if (!me || !secret) process.exit(1);
|
|
process.stdout.write(jwt.sign({ me, scope: "update" }, secret, { expiresIn: "5m" }));
|
|
' 2>/dev/null || true
|
|
)"
|
|
|
|
if [ -n "$TOKEN" ]; then
|
|
RESULT="$(curl -sS --max-time 300 -X POST -d "" "${WEBMENTION_ENDPOINT}?token=${TOKEN}" 2>&1 || true)"
|
|
echo "[webmention] $(date '+%Y-%m-%d %H:%M:%S') - ${RESULT:-ok}"
|
|
else
|
|
echo "[webmention] $(date '+%Y-%m-%d %H:%M:%S') - token generation failed"
|
|
fi
|
|
|
|
sleep "$WEBMENTION_POLL_INTERVAL"
|
|
done
|
|
) &
|
|
POLLER_PID="$!"
|
|
|
|
trap 'kill "${INDIEKIT_PID}" "${POLLER_PID}" 2>/dev/null || true; wait "${INDIEKIT_PID}" 2>/dev/null || true' EXIT INT TERM
|
|
|
|
wait "${INDIEKIT_PID}"
|