227 lines
8.6 KiB
Bash
227 lines
8.6 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
echo "[start-web] Starting web service..."
|
|
echo "[start-web] Bun: $(bun --version || true)"
|
|
|
|
cd /app
|
|
|
|
export BUN_INSTALL_CACHE_DIR="${BUN_INSTALL_CACHE_DIR:-/tmp/bun-cache}"
|
|
mkdir -p "$BUN_INSTALL_CACHE_DIR"
|
|
|
|
DB_PATH="/app/data/db.sqlite"
|
|
|
|
echo "[start-web] Using bun cache dir: $BUN_INSTALL_CACHE_DIR"
|
|
|
|
echo "[start-web] Using APP_DIR=$(pwd)"
|
|
echo "[start-web] NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-}"
|
|
echo "[start-web] NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL:-}"
|
|
echo "[start-web] CHAT_WS_PORT=${CHAT_WS_PORT:-3030}"
|
|
|
|
ensure_db_writable() {
|
|
mkdir -p "$(dirname "$DB_PATH")"
|
|
if [ ! -e "$DB_PATH" ]; then
|
|
touch "$DB_PATH" || true
|
|
chmod 660 "$DB_PATH" 2>/dev/null || true
|
|
fi
|
|
if ! touch "$DB_PATH" >/dev/null 2>&1; then
|
|
echo "[start-web] ERRO: não foi possível escrever em $DB_PATH (verifique permissões do volume /app/data)" >&2
|
|
ls -ld /app/data "$DB_PATH" >/dev/null 2>&1 || true
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
ensure_db_writable
|
|
|
|
# Ensure system deps for native modules (best-effort, idempotent)
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
if [ "${SKIP_APT_BOOTSTRAP:-false}" = "true" ]; then
|
|
echo "[start-web] SKIP_APT_BOOTSTRAP=true; skipping apt bootstrap"
|
|
else
|
|
# Ensure curl/gnupg for NodeSource setup
|
|
if ! command -v curl >/dev/null 2>&1; then
|
|
apt-get update -y || true
|
|
apt-get install -y --no-install-recommends curl ca-certificates gnupg || true
|
|
fi
|
|
|
|
install_node() {
|
|
local current_node_major=""
|
|
if command -v node >/dev/null 2>&1; then
|
|
current_node_major=$(node -v | sed -E 's/^v([0-9]+).*/\1/')
|
|
if [ -n "$current_node_major" ] && [ "$current_node_major" -ge 20 ]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
echo "[start-web] installing Node.js 22.x via NodeSource"
|
|
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - || true
|
|
apt-get update -y || true
|
|
apt-get install -y --no-install-recommends nodejs || true
|
|
}
|
|
|
|
# Base toolchain for native modules
|
|
apt-get update -y || true
|
|
apt-get install -y --no-install-recommends build-essential python3 make pkg-config || true
|
|
|
|
if ! command -v openssl >/dev/null 2>&1; then
|
|
echo "[start-web] openssl not found; installing via apt-get (requires root)"
|
|
apt-get update -y || true
|
|
apt-get install -y --no-install-recommends openssl ca-certificates || true
|
|
fi
|
|
if ! command -v node >/dev/null 2>&1 && [ -x /usr/bin/nodejs ]; then
|
|
ln -sf /usr/bin/nodejs /usr/bin/node || true
|
|
fi
|
|
install_node
|
|
fi
|
|
else
|
|
echo "[start-web] apt-get unavailable; skipping system deps install" >&2
|
|
fi
|
|
|
|
# Rebuild native better-sqlite3 bindings for the current Node version
|
|
if command -v npm >/dev/null 2>&1; then
|
|
check_better_sqlite3() {
|
|
node - <<'EOF'
|
|
const path = require("node:path")
|
|
try {
|
|
const pkgPath = require.resolve("better-sqlite3/package.json")
|
|
const pkg = require(pkgPath)
|
|
const binding = path.join(path.dirname(pkgPath), "build", "Release", "better_sqlite3.node")
|
|
require("better-sqlite3")
|
|
console.log(`[start-web] better-sqlite3 ok (v${pkg.version}) binding=${binding}`)
|
|
process.exit(0)
|
|
} catch (error) {
|
|
console.error("[start-web] better-sqlite3 load failed:", error?.message || error)
|
|
process.exit(1)
|
|
}
|
|
EOF
|
|
}
|
|
|
|
copy_fallback_binding() {
|
|
local temp_dir="/tmp/bsql-fallback"
|
|
rm -rf "$temp_dir"
|
|
mkdir -p "$temp_dir"
|
|
(
|
|
cd "$temp_dir" &&
|
|
npm install better-sqlite3@11.10.0 --build-from-source --no-save >/dev/null 2>&1
|
|
) || {
|
|
echo "[start-web] fallback npm install falhou"
|
|
return 1
|
|
}
|
|
local src_pkg="$temp_dir/node_modules/better-sqlite3"
|
|
local dest_pkg="/app/node_modules/.bun/better-sqlite3@11.10.0/node_modules/better-sqlite3"
|
|
mkdir -p "$dest_pkg"
|
|
cp -R "$src_pkg/"* "$dest_pkg/" || return 1
|
|
echo "[start-web] fallback: pacote better-sqlite3 copiado para .bun store"
|
|
return 0
|
|
}
|
|
|
|
rebuild_and_repin_sqlite() {
|
|
echo "[start-web] rebuilding better-sqlite3 para a runtime atual"
|
|
npm rebuild better-sqlite3 --build-from-source >/dev/null 2>&1 || {
|
|
echo "[start-web] rebuild falhou; tentando fallback" >&2
|
|
copy_fallback_binding || echo "[start-web] fallback também falhou" >&2
|
|
}
|
|
node - <<'EOF'
|
|
const fs = require("node:fs")
|
|
const path = require("node:path")
|
|
try {
|
|
const pkgPath = require.resolve("better-sqlite3/package.json")
|
|
const pkgDir = path.dirname(pkgPath)
|
|
const pkg = require(pkgPath)
|
|
const built = path.join(pkgDir, "build", "Release", "better_sqlite3.node")
|
|
|
|
// Copy to bun store
|
|
const store = path.join(process.cwd(), "node_modules", ".bun", `better-sqlite3@${pkg.version}`, "node_modules", "better-sqlite3", "build", "Release", "better_sqlite3.node")
|
|
fs.mkdirSync(path.dirname(store), { recursive: true })
|
|
fs.copyFileSync(built, store)
|
|
console.log(`[start-web] better-sqlite3 (v${pkg.version}) copiado para store bun: ${store}`)
|
|
|
|
// Copy to @prisma/adapter-better-sqlite3 and @prisma/client locations
|
|
const nodeVersion = process.version.slice(1).split('.')[0] + '.' + process.version.slice(1).split('.')[1] + '.' + process.version.slice(1).split('.')[2]
|
|
const adapterPaths = [
|
|
// Adapter locations
|
|
path.join(process.cwd(), "node_modules", "@prisma", "adapter-better-sqlite3", "build", "Release", "better_sqlite3.node"),
|
|
path.join(process.cwd(), "node_modules", "@prisma", "adapter-better-sqlite3", "node_modules", "better-sqlite3", "build", "Release", "better_sqlite3.node"),
|
|
// @prisma/client locations that the adapter might be looking at
|
|
path.join(process.cwd(), "node_modules", "@prisma", "client", "build", "Release", "better_sqlite3.node"),
|
|
path.join(process.cwd(), "node_modules", "@prisma", "client", "Release", "better_sqlite3.node"),
|
|
path.join(process.cwd(), "node_modules", "@prisma", "client", "compiled", nodeVersion, "linux", "x64", "better_sqlite3.node"),
|
|
// Bun store locations
|
|
path.join(process.cwd(), "node_modules", ".bun", "@prisma-adapter-better-sqlite3@7.0.0", "node_modules", "@prisma", "adapter-better-sqlite3", "node_modules", "better-sqlite3", "build", "Release", "better_sqlite3.node"),
|
|
]
|
|
for (const dest of adapterPaths) {
|
|
try {
|
|
fs.mkdirSync(path.dirname(dest), { recursive: true })
|
|
fs.copyFileSync(built, dest)
|
|
console.log(`[start-web] binding copiado para: ${dest}`)
|
|
} catch (e) {
|
|
// Ignore path copy errors
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("[start-web] não foi possível copiar binding para .bun store:", error?.message || error)
|
|
}
|
|
EOF
|
|
}
|
|
|
|
if [ "${SKIP_SQLITE_REBUILD:-false}" = "true" ]; then
|
|
echo "[start-web] SKIP_SQLITE_REBUILD=true; tentando usar bindings existentes"
|
|
if ! check_better_sqlite3; then
|
|
echo "[start-web] bindings inválidos; forçando rebuild mesmo com SKIP_SQLITE_REBUILD=true"
|
|
rebuild_and_repin_sqlite
|
|
check_better_sqlite3 || {
|
|
echo "[start-web] ERRO: better-sqlite3 continua inválido após rebuild" >&2
|
|
exit 1
|
|
}
|
|
fi
|
|
else
|
|
rebuild_and_repin_sqlite
|
|
check_better_sqlite3 || {
|
|
echo "[start-web] ERRO: better-sqlite3 inválido após rebuild; tentando fallback"
|
|
copy_fallback_binding && check_better_sqlite3 || {
|
|
echo "[start-web] ERRO: better-sqlite3 continua inválido após fallback" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
fi
|
|
fi
|
|
|
|
# Bun keeps its store in node_modules/.bun by default; ensure it exists and is writable
|
|
mkdir -p node_modules/.bun >/dev/null 2>&1 || true
|
|
|
|
# Prisma generate (idempotent) and apply DB migrations
|
|
echo "[start-web] prisma generate (bun runtime)"
|
|
bunx --bun prisma generate
|
|
|
|
echo "[start-web] prisma migrate deploy (bun runtime)"
|
|
bunx --bun prisma migrate deploy
|
|
|
|
# Seed Better Auth users safely (ensure-only by default)
|
|
if [ "${SKIP_AUTH_SEED:-false}" != "true" ]; then
|
|
echo "[start-web] seeding Better Auth users (ensure-only)"
|
|
bun run auth:seed || true
|
|
else
|
|
echo "[start-web] skipping auth seed (SKIP_AUTH_SEED=true)"
|
|
fi
|
|
|
|
# Iniciar servidor WebSocket de chat (processo dedicado)
|
|
CHAT_WS_PORT="${CHAT_WS_PORT:-3030}"
|
|
CHAT_WS_SCRIPT="/app/scripts/chat-ws-server.mjs"
|
|
if [ -f "$CHAT_WS_SCRIPT" ]; then
|
|
echo "[start-web] iniciando chat-ws-server em :$CHAT_WS_PORT"
|
|
node "$CHAT_WS_SCRIPT" &
|
|
CHAT_WS_PID=$!
|
|
# Garantir cleanup
|
|
trap "kill $CHAT_WS_PID 2>/dev/null || true" EXIT
|
|
else
|
|
echo "[start-web] chat-ws-server não encontrado em $CHAT_WS_SCRIPT" >&2
|
|
fi
|
|
|
|
echo "[start-web] launching Next.js"
|
|
PORT=${PORT:-3000}
|
|
NODE_MAJOR=$(command -v node >/dev/null 2>&1 && node -v | sed -E 's/^v([0-9]+).*/\1/' || echo "")
|
|
if [ -z "$NODE_MAJOR" ] || [ "$NODE_MAJOR" -lt 20 ]; then
|
|
echo "[start-web] Node.js 20+ not available; aborting"
|
|
exit 1
|
|
fi
|
|
exec node node_modules/next/dist/bin/next start --port "$PORT"
|