源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
Executable
+308
@@ -0,0 +1,308 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Start the Den server stack inside a Daytona sandbox.
|
||||
# Services: MySQL, Den API, and Den Web.
|
||||
|
||||
if [ -n "${OPENWORK_WORKSPACE_DIR:-}" ]; then
|
||||
REPO_DIR="$OPENWORK_WORKSPACE_DIR"
|
||||
elif [ -f /workspace/package.json ]; then
|
||||
REPO_DIR="/workspace"
|
||||
else
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
fi
|
||||
|
||||
cd "$REPO_DIR"
|
||||
|
||||
DEN_API_PORT="${DEN_API_PORT:-8788}"
|
||||
DEN_WEB_PORT="${DEN_WEB_PORT:-3005}"
|
||||
PNPM_STORE="${PNPM_STORE:-$REPO_DIR/.openwork-daytona/pnpm-store}"
|
||||
|
||||
DEN_API_PUBLIC_URL="${DEN_API_PUBLIC_URL:-http://localhost:$DEN_API_PORT}"
|
||||
DEN_WEB_PUBLIC_URL="${DEN_WEB_PUBLIC_URL:-http://localhost:$DEN_WEB_PORT}"
|
||||
DEN_WEB_PUBLIC_HOST="${DEN_WEB_PUBLIC_URL#http://}"
|
||||
DEN_WEB_PUBLIC_HOST="${DEN_WEB_PUBLIC_HOST#https://}"
|
||||
DEN_WEB_PUBLIC_HOST="${DEN_WEB_PUBLIC_HOST%%/*}"
|
||||
|
||||
export OPENWORK_DEV_MODE="${OPENWORK_DEV_MODE:-1}"
|
||||
export DEN_ORG_MODE="${DEN_ORG_MODE:-multi_org}"
|
||||
# Eval sign-ups must not depend on the HIBP API.
|
||||
export DEN_PASSWORD_BREACH_SCREENING_ENABLED="${DEN_PASSWORD_BREACH_SCREENING_ENABLED:-false}"
|
||||
export DEN_GENERATED_ARTIFACT_VIEWS_ENABLED="${DEN_GENERATED_ARTIFACT_VIEWS_ENABLED:-false}"
|
||||
export DATABASE_URL="${DATABASE_URL:-mysql://root:password@127.0.0.1:3306/openwork_den}"
|
||||
export DEN_DB_ENCRYPTION_KEY="${DEN_DB_ENCRYPTION_KEY:-daytona-den-db-encryption-key-please-change-1234567890}"
|
||||
export BETTER_AUTH_SECRET="${BETTER_AUTH_SECRET:-daytona-den-auth-secret-please-change-1234567890}"
|
||||
export BETTER_AUTH_URL="${BETTER_AUTH_URL:-$DEN_WEB_PUBLIC_URL}"
|
||||
export DEN_BETTER_AUTH_URL="$BETTER_AUTH_URL"
|
||||
export DEN_API_PUBLIC_URL
|
||||
export DEN_MCP_RESOURCE_URL="${DEN_MCP_RESOURCE_URL:-$DEN_API_PUBLIC_URL/mcp}"
|
||||
# DEN_API_BASE is the externally reachable Den API origin (see
|
||||
# packages/docs/start-here/private-network-deployment.mdx): Den Web publishes
|
||||
# it through /api/runtime-config as the desktop's denApiUrl. Desktops run in
|
||||
# other sandboxes and can only reach the public preview URL, never this
|
||||
# sandbox's loopback.
|
||||
export DEN_API_BASE="${DEN_API_BASE:-${DEN_API_PUBLIC_URL:-http://127.0.0.1:$DEN_API_PORT}}"
|
||||
export DEN_AUTH_ORIGIN="${DEN_AUTH_ORIGIN:-$DEN_WEB_PUBLIC_URL}"
|
||||
export DEN_AUTH_FALLBACK_BASE="${DEN_AUTH_FALLBACK_BASE:-http://127.0.0.1:$DEN_API_PORT}"
|
||||
export NEXT_PUBLIC_OPENWORK_AUTH_CALLBACK_URL="${NEXT_PUBLIC_OPENWORK_AUTH_CALLBACK_URL:-$DEN_WEB_PUBLIC_URL}"
|
||||
export DEN_PROVISIONER_MODE="${DEN_PROVISIONER_MODE:-stub}"
|
||||
export DEN_WORKER_URL_TEMPLATE="${DEN_WORKER_URL_TEMPLATE:-https://workers.local/{workerId}}"
|
||||
export DEN_WEB_ALLOWED_DEV_ORIGINS="${DEN_WEB_ALLOWED_DEV_ORIGINS:-$DEN_WEB_PUBLIC_HOST}"
|
||||
|
||||
DEFAULT_ORIGINS="$DEN_WEB_PUBLIC_URL,$DEN_API_PUBLIC_URL,http://localhost:$DEN_WEB_PORT,http://127.0.0.1:$DEN_WEB_PORT,http://localhost:$DEN_API_PORT,http://127.0.0.1:$DEN_API_PORT"
|
||||
export CORS_ORIGINS="${CORS_ORIGINS:-$DEFAULT_ORIGINS}"
|
||||
|
||||
# Daytona mints a fresh preview hostname on every preview-url call, so any
|
||||
# origin baked at boot goes stale immediately. Trust the preview proxy domain
|
||||
# by wildcard (better-auth supports wildcard trusted origins) so rotated
|
||||
# preview URLs keep working. Local hosts produce no wildcard.
|
||||
PREVIEW_PROXY_HOST="${DEN_WEB_PUBLIC_URL#http://}"
|
||||
PREVIEW_PROXY_HOST="${PREVIEW_PROXY_HOST#https://}"
|
||||
PREVIEW_PROXY_HOST="${PREVIEW_PROXY_HOST%%/*}"
|
||||
PREVIEW_PROXY_WILDCARD=""
|
||||
case "$PREVIEW_PROXY_HOST" in
|
||||
localhost*|127.*|0.0.0.0*|\[*) ;;
|
||||
*.*.*)
|
||||
PREVIEW_PROXY_WILDCARD="https://*.${PREVIEW_PROXY_HOST#*.}"
|
||||
;;
|
||||
esac
|
||||
# Preview hosts are not app.*: tell den-api the preview domain serves den-web
|
||||
# so desktop handoff links point at the den-web /api/den proxy.
|
||||
export DEN_WEB_APP_HOSTS="${DEN_WEB_APP_HOSTS:-${PREVIEW_PROXY_WILDCARD:+.${PREVIEW_PROXY_HOST#*.}}}"
|
||||
export DEN_BETTER_AUTH_TRUSTED_ORIGINS="${DEN_BETTER_AUTH_TRUSTED_ORIGINS:-$CORS_ORIGINS${PREVIEW_PROXY_WILDCARD:+,$PREVIEW_PROXY_WILDCARD}}"
|
||||
|
||||
run_root() {
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
"$@"
|
||||
elif command -v sudo >/dev/null 2>&1; then
|
||||
sudo "$@"
|
||||
else
|
||||
echo "ERROR: root privileges are required to start MySQL." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
MYSQL_ROOT_CMD=(run_root mysql -uroot)
|
||||
|
||||
wait_for_http() {
|
||||
local url="$1"
|
||||
local label="$2"
|
||||
local max_wait="${3:-180}"
|
||||
local elapsed=0
|
||||
|
||||
while [ "$elapsed" -lt "$max_wait" ]; do
|
||||
if curl -sf "$url" >/dev/null 2>&1; then
|
||||
echo "==> $label ready after ${elapsed}s"
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
elapsed=$((elapsed + 5))
|
||||
done
|
||||
|
||||
echo "ERROR: $label did not become ready at $url" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "==> Starting MySQL..."
|
||||
run_root service mysql start >/tmp/openwork-mysql-service.log 2>&1 || run_root service mariadb start >/tmp/openwork-mysql-service.log 2>&1
|
||||
|
||||
for _ in $(seq 1 60); do
|
||||
if mysql -uroot -ppassword -e "SELECT 1" >/dev/null 2>&1; then
|
||||
MYSQL_ROOT_CMD=(mysql -uroot -ppassword)
|
||||
break
|
||||
fi
|
||||
if run_root mysql -uroot -e "SELECT 1" >/dev/null 2>&1; then
|
||||
MYSQL_ROOT_CMD=(run_root mysql -uroot)
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
"${MYSQL_ROOT_CMD[@]}" <<'SQL'
|
||||
CREATE DATABASE IF NOT EXISTS openwork_den;
|
||||
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';
|
||||
CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY 'password';
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
|
||||
FLUSH PRIVILEGES;
|
||||
SQL
|
||||
|
||||
echo "==> Installing dependencies if needed..."
|
||||
mkdir -p "$PNPM_STORE" .openwork-daytona
|
||||
baseline=.openwork-daytona/pnpm-lock.sha256
|
||||
current="$(sha256sum pnpm-lock.yaml | cut -d " " -f 1)"
|
||||
# The server stack never runs Electron or browser automation; skip their
|
||||
# binary downloads on reinstalls too.
|
||||
INSTALL_ENV=(CI=1 ELECTRON_SKIP_BINARY_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 PUPPETEER_SKIP_DOWNLOAD=1 CYPRESS_INSTALL_BINARY=0)
|
||||
if [ ! -d node_modules ] || [ ! -f "$baseline" ] || [ "$(cat "$baseline")" != "$current" ]; then
|
||||
env "${INSTALL_ENV[@]}" pnpm install --store-dir "$PNPM_STORE" --frozen-lockfile \
|
||||
|| env "${INSTALL_ENV[@]}" pnpm install --store-dir "$PNPM_STORE"
|
||||
printf "%s" "$current" > "$baseline"
|
||||
else
|
||||
echo "==> Skipping pnpm install (node_modules present and lockfile unchanged)."
|
||||
fi
|
||||
|
||||
# Content keys for the sandbox-invariant builds baked into the snapshot.
|
||||
# A build is skipped only when the git trees that feed it and the lockfile
|
||||
# both match what the snapshot (or a previous boot) built. Snapshots without
|
||||
# markers, refs that changed those trees, or refs where a tree is missing
|
||||
# rebuild exactly as before. An empty key never matches and is never stored.
|
||||
build_key() {
|
||||
local trees
|
||||
trees="$(git rev-parse "$@" 2>/dev/null)" || return 0
|
||||
# Byte-identical to the snapshot bake: one tree hash per line, then the
|
||||
# lockfile sha, all newline-terminated.
|
||||
printf '%s\n%s\n' "$trees" "$current" | sha256sum | cut -d ' ' -f 1
|
||||
}
|
||||
|
||||
echo "==> Pushing Den DB schema..."
|
||||
pnpm --filter @openwork-ee/den-db db:push > /tmp/den-db-push.log 2>&1
|
||||
|
||||
den_api_assets_marker=.openwork-daytona/den-api-assets.tree
|
||||
den_api_assets_key="$(build_key HEAD:packages/mcp-apps)"
|
||||
if [ -n "$den_api_assets_key" ] && [ -d packages/mcp-apps/dist ] && [ -f "$den_api_assets_marker" ] \
|
||||
&& [ "$(cat "$den_api_assets_marker")" = "$den_api_assets_key" ]; then
|
||||
echo "==> Skipping Den API runtime asset build (baked assets match this ref)."
|
||||
else
|
||||
echo "==> Building Den API runtime assets..."
|
||||
pnpm --filter @openwork-ee/den-api run build:mcp-apps
|
||||
if [ -n "$den_api_assets_key" ]; then
|
||||
printf "%s" "$den_api_assets_key" > "$den_api_assets_marker"
|
||||
else
|
||||
rm -f "$den_api_assets_marker"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "==> Starting Den API on :$DEN_API_PORT..."
|
||||
# The den-api process cmdline is "tsx watch src/main.ts" (cwd-relative), so a
|
||||
# pattern anchored on the repo path never matches and restarts silently keep
|
||||
# the old server alive on the port. main.ts is unique to den-api here.
|
||||
pkill -f "tsx watch src/main.ts" >/dev/null 2>&1 || true
|
||||
nohup env \
|
||||
PORT="$DEN_API_PORT" \
|
||||
DATABASE_URL="$DATABASE_URL" \
|
||||
DEN_DB_ENCRYPTION_KEY="$DEN_DB_ENCRYPTION_KEY" \
|
||||
BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \
|
||||
BETTER_AUTH_URL="$BETTER_AUTH_URL" \
|
||||
DEN_API_PUBLIC_URL="$DEN_API_PUBLIC_URL" \
|
||||
DEN_WEB_APP_HOSTS="$DEN_WEB_APP_HOSTS" \
|
||||
DEN_MCP_RESOURCE_URL="$DEN_MCP_RESOURCE_URL" \
|
||||
DEN_BETTER_AUTH_TRUSTED_ORIGINS="$DEN_BETTER_AUTH_TRUSTED_ORIGINS" \
|
||||
CORS_ORIGINS="$CORS_ORIGINS" \
|
||||
DEN_BOOTSTRAP_ADMIN_EMAILS="${DEN_BOOTSTRAP_ADMIN_EMAILS:-}" \
|
||||
PROVISIONER_MODE="$DEN_PROVISIONER_MODE" \
|
||||
WORKER_URL_TEMPLATE="$DEN_WORKER_URL_TEMPLATE" \
|
||||
DEN_ORG_MODE="$DEN_ORG_MODE" \
|
||||
DEN_PASSWORD_BREACH_SCREENING_ENABLED="$DEN_PASSWORD_BREACH_SCREENING_ENABLED" \
|
||||
DEN_GENERATED_ARTIFACT_VIEWS_ENABLED="$DEN_GENERATED_ARTIFACT_VIEWS_ENABLED" \
|
||||
OPENWORK_DEV_MODE="$OPENWORK_DEV_MODE" \
|
||||
NODE_OPTIONS="--conditions=development" \
|
||||
pnpm --filter @openwork-ee/den-api exec tsx watch src/main.ts > /tmp/den-api.log 2>&1 &
|
||||
|
||||
wait_for_http "http://127.0.0.1:$DEN_API_PORT/health" "Den API" 180
|
||||
|
||||
if [ "${RUN_SEED:-0}" = "1" ]; then
|
||||
demo_email="${DEN_DEMO_OWNER_EMAIL:-alex@acme.test}"
|
||||
demo_password="${DEN_DEMO_OWNER_PASSWORD:-OpenWorkDemo123!}"
|
||||
signin_ok() {
|
||||
curl -sf -o /dev/null -X POST "http://127.0.0.1:$DEN_API_PORT/api/auth/sign-in/email" \
|
||||
-H 'content-type: application/json' \
|
||||
-d "{\"email\":\"$demo_email\",\"password\":\"$demo_password\"}"
|
||||
}
|
||||
if signin_ok; then
|
||||
echo "==> Demo org already present ($demo_email)"
|
||||
else
|
||||
echo "==> Seeding demo org..."
|
||||
(cd ee/apps/den-api && env \
|
||||
DATABASE_URL="$DATABASE_URL" \
|
||||
DEN_DB_ENCRYPTION_KEY="$DEN_DB_ENCRYPTION_KEY" \
|
||||
BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \
|
||||
BETTER_AUTH_URL="$BETTER_AUTH_URL" \
|
||||
DEN_API_PUBLIC_URL="$DEN_API_PUBLIC_URL" \
|
||||
DEN_ORG_MODE="$DEN_ORG_MODE" \
|
||||
OPENWORK_DEV_MODE="$OPENWORK_DEV_MODE" \
|
||||
DEN_DEMO_SEED_FETCH_GITHUB="${DEN_DEMO_SEED_FETCH_GITHUB:-0}" \
|
||||
node --conditions=development --import tsx scripts/seed-demo-org.ts) > /tmp/den-seed.log 2>&1
|
||||
if signin_ok; then
|
||||
echo "==> Demo org seeded ($demo_email)"
|
||||
else
|
||||
echo "ERROR: seed completed but $demo_email cannot sign in. See /tmp/den-seed.log" >&2
|
||||
tail -40 /tmp/den-seed.log >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo "DEMO_OWNER_READY=$demo_email"
|
||||
fi
|
||||
|
||||
den_web_marker=.openwork-daytona/den-web-build.tree
|
||||
den_web_key="$(build_key HEAD:packages/ui HEAD:ee/packages/utils HEAD:ee/apps/den-web)"
|
||||
if [ -n "$den_web_key" ] && [ -d ee/apps/den-web/.next ] && [ -f "$den_web_marker" ] \
|
||||
&& [ "$(cat "$den_web_marker")" = "$den_web_key" ]; then
|
||||
echo "==> Skipping Den Web build (baked build matches this ref)."
|
||||
else
|
||||
# Production build instead of next dev: dev-mode HMR websockets 502 through
|
||||
# the preview proxy and block hydration. The build bakes no per-sandbox
|
||||
# values (all DEN_* env is consumed at runtime by next start), so a build
|
||||
# from the snapshot's base commit is reusable across sandboxes.
|
||||
echo "==> Building Den Web..."
|
||||
if ! env \
|
||||
DEN_WEB_PORT="$DEN_WEB_PORT" \
|
||||
DEN_API_BASE="$DEN_API_BASE" \
|
||||
DEN_AUTH_ORIGIN="$DEN_AUTH_ORIGIN" \
|
||||
DEN_AUTH_FALLBACK_BASE="$DEN_AUTH_FALLBACK_BASE" \
|
||||
NEXT_PUBLIC_OPENWORK_AUTH_CALLBACK_URL="$NEXT_PUBLIC_OPENWORK_AUTH_CALLBACK_URL" \
|
||||
NEXT_PUBLIC_POSTHOG_KEY= \
|
||||
NEXT_PUBLIC_POSTHOG_API_KEY= \
|
||||
DEN_ORG_MODE="$DEN_ORG_MODE" \
|
||||
OPENWORK_DEV_MODE="$OPENWORK_DEV_MODE" \
|
||||
DEN_WEB_ALLOWED_DEV_ORIGINS="$DEN_WEB_ALLOWED_DEV_ORIGINS" \
|
||||
bash -c 'pnpm --filter @openwork/ui build && pnpm --filter @openwork-ee/utils build && pnpm --filter @openwork-ee/den-web build' > /tmp/den-web-build.log 2>&1; then
|
||||
echo "ERROR: Den Web build failed. Last 80 lines:" >&2
|
||||
tail -n 80 /tmp/den-web-build.log >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$den_web_key" ]; then
|
||||
printf "%s" "$den_web_key" > "$den_web_marker"
|
||||
else
|
||||
rm -f "$den_web_marker"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "==> Starting Den Web on :$DEN_WEB_PORT..."
|
||||
pkill -f "next dev --hostname 0.0.0.0 --port $DEN_WEB_PORT" >/dev/null 2>&1 || true
|
||||
pkill -f "next start --hostname 0.0.0.0 --port $DEN_WEB_PORT" >/dev/null 2>&1 || true
|
||||
nohup env \
|
||||
DEN_WEB_PORT="$DEN_WEB_PORT" \
|
||||
DEN_API_BASE="$DEN_API_BASE" \
|
||||
DEN_AUTH_ORIGIN="$DEN_AUTH_ORIGIN" \
|
||||
DEN_AUTH_FALLBACK_BASE="$DEN_AUTH_FALLBACK_BASE" \
|
||||
NEXT_PUBLIC_OPENWORK_AUTH_CALLBACK_URL="$NEXT_PUBLIC_OPENWORK_AUTH_CALLBACK_URL" \
|
||||
NEXT_PUBLIC_POSTHOG_KEY= \
|
||||
NEXT_PUBLIC_POSTHOG_API_KEY= \
|
||||
DEN_ORG_MODE="$DEN_ORG_MODE" \
|
||||
OPENWORK_DEV_MODE="$OPENWORK_DEV_MODE" \
|
||||
DEN_WEB_ALLOWED_DEV_ORIGINS="$DEN_WEB_ALLOWED_DEV_ORIGINS" \
|
||||
pnpm --filter @openwork-ee/den-web exec next start --hostname 0.0.0.0 --port "$DEN_WEB_PORT" > /tmp/den-web.log 2>&1 &
|
||||
|
||||
wait_for_http "http://127.0.0.1:$DEN_WEB_PORT/api/den/health" "Den Web" 180
|
||||
|
||||
cat > .openwork-daytona/server-env <<EOF
|
||||
DEN_API_URL=$DEN_API_PUBLIC_URL
|
||||
DEN_WEB_URL=$DEN_WEB_PUBLIC_URL
|
||||
BETTER_AUTH_URL=$BETTER_AUTH_URL
|
||||
DEN_MCP_RESOURCE_URL=$DEN_MCP_RESOURCE_URL
|
||||
DEN_PROVISIONER_MODE=$DEN_PROVISIONER_MODE
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " OpenWork Daytona server stack ready"
|
||||
echo ""
|
||||
echo " Den Web: $DEN_WEB_PUBLIC_URL"
|
||||
echo " Den API: $DEN_API_PUBLIC_URL"
|
||||
echo ""
|
||||
echo " Logs:"
|
||||
echo " /tmp/den-api.log"
|
||||
echo " /tmp/den-web.log"
|
||||
echo " /tmp/den-db-push.log"
|
||||
echo "============================================"
|
||||
Reference in New Issue
Block a user