The "Active WebUI" card (Settings > Web Applications) only showed
description and __APP_VERSION__, never the resourceUrl it's actually
built from — add it as a linked "Source" row.
That card also turned out to be misleading in local dev: it reads the
x:Application record whose urlPrefix matches /admin or /account, and a
freshly bootstrapped server keeps Stalwart's seeded default there
("Stalwart Web Interface", pointing at stalwartlabs/webui's release),
regardless of what's actually being served — in dev that's this fork,
served directly by Vite, which never touches that record at all.
dev-server-init.sh/.ps1 now point that record's description and
resourceUrl at this fork's own release, so the card (now including the
visible Source URL) reflects what's actually running instead of
Stalwart's factory default.
Verified end-to-end against a fresh container with both scripts.
77 lines
5.0 KiB
Bash
77 lines
5.0 KiB
Bash
#!/usr/bin/env bash
|
|
# Local development only. One-time setup for the disposable Stalwart test
|
|
# server from docker-compose.yml: completes the server's bootstrap wizard,
|
|
# creates a real "devadmin" account (used by dev-token.sh/.ps1 — the
|
|
# STALWART_RECOVERY_ADMIN account is break-glass only and always issues
|
|
# fixed 1h OAuth tokens regardless of server config), and sets the default
|
|
# OAuth access token lifetime to 3 hours.
|
|
#
|
|
# Idempotent: safe to re-run; does nothing if the server already left
|
|
# bootstrap mode. Run this once after `npm run dev:server` on a fresh
|
|
# volume (or after `docker compose down -v`).
|
|
set -euo pipefail
|
|
|
|
API_BASE_URL="${1:-http://localhost:8080}"
|
|
RECOVERY_ACCOUNT="admin@example.org"
|
|
RECOVERY_SECRET="c8321iEscHDy0GWV"
|
|
DEV_DOMAIN="example.org"
|
|
DEV_HOSTNAME="mail.example.org"
|
|
DEVADMIN_NAME="devadmin"
|
|
DEVADMIN_SECRET="DevAdminPass123!"
|
|
DEFAULT_TOKEN_EXPIRY_MS=10800000 # 3 hours
|
|
|
|
jmap() {
|
|
curl -sf --compressed -u "$RECOVERY_ACCOUNT:$RECOVERY_SECRET" \
|
|
-X POST -H "Content-Type: application/json" -d "$1" "$API_BASE_URL/jmap/"
|
|
}
|
|
|
|
echo "Waiting for $API_BASE_URL to be reachable..."
|
|
for _ in $(seq 1 30); do
|
|
curl -sf -o /dev/null -u "$RECOVERY_ACCOUNT:$RECOVERY_SECRET" "$API_BASE_URL/jmap/session" && break
|
|
sleep 1
|
|
done
|
|
|
|
SESSION=$(curl -sf --compressed -u "$RECOVERY_ACCOUNT:$RECOVERY_SECRET" "$API_BASE_URL/jmap/session")
|
|
ACCOUNT_ID=$(printf '%s' "$SESSION" | grep -o '"urn:stalwart:jmap":"[^"]*"' | cut -d'"' -f4)
|
|
|
|
QUERY_RESULT=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Domain/query\",{\"accountId\":\"$ACCOUNT_ID\"},\"0\"]]}")
|
|
if ! printf '%s' "$QUERY_RESULT" | grep -q '"forbidden"'; then
|
|
echo "Server already bootstrapped, skipping setup. (Use 'docker compose down -v && npm run dev:server' to start fresh.)"
|
|
else
|
|
echo "Completing server bootstrap (domain: $DEV_DOMAIN, no TLS certificate request)..."
|
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Bootstrap/set\",{\"accountId\":\"$ACCOUNT_ID\",\"update\":{\"singleton\":{\"defaultDomain\":\"$DEV_DOMAIN\",\"serverHostname\":\"$DEV_HOSTNAME\",\"requestTlsCertificate\":false}}},\"0\"]]}" > /dev/null
|
|
|
|
echo "Restarting the container to apply bootstrap config (one-time only)..."
|
|
docker compose restart stalwart > /dev/null
|
|
for _ in $(seq 1 30); do
|
|
curl -sf -o /dev/null -u "$RECOVERY_ACCOUNT:$RECOVERY_SECRET" "$API_BASE_URL/jmap/session" && break
|
|
sleep 1
|
|
done
|
|
|
|
DOMAIN_RESULT=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Domain/query\",{\"accountId\":\"$ACCOUNT_ID\"},\"0\"]]}")
|
|
DOMAIN_ID=$(printf '%s' "$DOMAIN_RESULT" | grep -o '"ids":\["[^"]*"' | cut -d'"' -f4)
|
|
|
|
echo "Creating devadmin account ($DEVADMIN_NAME@$DEV_DOMAIN)..."
|
|
CREATE_RESULT=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Account/set\",{\"accountId\":\"$ACCOUNT_ID\",\"create\":{\"u1\":{\"@type\":\"User\",\"name\":\"$DEVADMIN_NAME\",\"domainId\":\"$DOMAIN_ID\",\"roles\":{\"@type\":\"Admin\"}}}},\"0\"]]}")
|
|
DEVADMIN_ID=$(printf '%s' "$CREATE_RESULT" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
|
|
|
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Account/set\",{\"accountId\":\"$ACCOUNT_ID\",\"update\":{\"$DEVADMIN_ID\":{\"credentials\":{\"0\":{\"@type\":\"Password\",\"secret\":\"$DEVADMIN_SECRET\"}}}}},\"0\"]]}" > /dev/null
|
|
|
|
echo "Setting default OAuth access token lifetime to 3 hours..."
|
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:OidcProvider/set\",{\"accountId\":\"$ACCOUNT_ID\",\"update\":{\"singleton\":{\"accessTokenExpiry\":$DEFAULT_TOKEN_EXPIRY_MS}}},\"0\"]]}" > /dev/null
|
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Action/set\",{\"accountId\":\"$ACCOUNT_ID\",\"create\":{\"a1\":{\"@type\":\"ReloadSettings\"}}},\"0\"]]}" > /dev/null 2>&1 || true
|
|
|
|
# Cosmetic only: Stalwart seeds a default "Stalwart Web Interface" /
|
|
# stalwartlabs/webui entry for the x:Application record serving /admin
|
|
# and /account (see Settings > Web Applications' "Active WebUI" card).
|
|
# Point it at this fork so that card isn't misleading in local dev too.
|
|
echo "Pointing the active WebUI's description at this fork..."
|
|
APP_RESULT=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Application/query\",{\"accountId\":\"$ACCOUNT_ID\"},\"0\"]]}") || true
|
|
APP_ID=$(printf '%s' "$APP_RESULT" | grep -o '"ids":\["[^"]*"' | cut -d'"' -f4)
|
|
if [ -n "$APP_ID" ]; then
|
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Application/set\",{\"accountId\":\"$ACCOUNT_ID\",\"update\":{\"$APP_ID\":{\"description\":\"Stalwart WebUI Fork\",\"resourceUrl\":\"https://github.com/LinkPhoenix/stalwart-webui-fork/releases/latest/download/webui.zip\"}}},\"0\"]]}" > /dev/null 2>&1 || true
|
|
fi
|
|
|
|
echo "Done. devadmin@$DEV_DOMAIN / $DEVADMIN_SECRET is ready — run scripts/dev-token.sh to get a token."
|
|
fi
|