diff --git a/.cursor/environment.json b/.cursor/environment.json new file mode 100644 index 00000000000..7df7f61ebb4 --- /dev/null +++ b/.cursor/environment.json @@ -0,0 +1,17 @@ +{ + "name": "nodejs-goof", + "install": "bash .cursor/install.sh", + "start": "bash .cursor/start.sh", + "terminals": [ + { + "name": "goof", + "command": "npm start" + } + ], + "ports": [ + { + "name": "web", + "port": 3001 + } + ] +} diff --git a/.cursor/install.sh b/.cursor/install.sh new file mode 100755 index 00000000000..d7bbd20bb38 --- /dev/null +++ b/.cursor/install.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Idempotent dependency setup for the Goof Cloud Agent environment. +# Installs Docker (used to run the mongo:3 + mysql:5 databases the app needs), +# the fuse-overlayfs storage driver (the default overlayfs snapshotter fails in +# the nested Cloud Agent VM), and the Node.js dependencies. Best-effort pre-pull +# of the database images so a fresh boot starts quickly. +set -euo pipefail + +cd "$(dirname "$0")/.." + +export DEBIAN_FRONTEND=noninteractive + +# --- System packages: Docker + fuse-overlayfs --- +if ! command -v docker >/dev/null 2>&1 || ! command -v fuse-overlayfs >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y docker.io fuse-overlayfs +fi + +# The containerd/overlayfs snapshotter cannot extract images inside the nested +# Cloud Agent VM, so pin the classic graph driver backed by fuse-overlayfs. +sudo mkdir -p /etc/docker +echo '{"features":{"containerd-snapshotter":false},"storage-driver":"fuse-overlayfs"}' \ + | sudo tee /etc/docker/daemon.json >/dev/null + +# --- Node dependencies --- +npm install + +# --- Best-effort pre-pull of database images (cached on disk for fast boots) --- +# Non-fatal: if the daemon cannot run during the build phase, start.sh pulls on boot. +STARTED_DOCKERD=0 +if ! sudo docker info >/dev/null 2>&1; then + sudo rm -f /var/run/docker.pid + sudo setsid dockerd >/tmp/dockerd-install.log 2>&1 & + for _ in $(seq 1 30); do sudo docker info >/dev/null 2>&1 && break; sleep 1; done + STARTED_DOCKERD=1 +fi +if sudo docker info >/dev/null 2>&1; then + sudo docker pull mongo:3 || true + sudo docker pull mysql:5 || true +fi +# Stop the daemon only if this script started it (leave a pre-existing one alone). +if [ "$STARTED_DOCKERD" = "1" ] && [ -f /var/run/docker.pid ]; then + sudo kill "$(cat /var/run/docker.pid)" 2>/dev/null || true +fi + +echo "install.sh complete" diff --git a/.cursor/start.sh b/.cursor/start.sh new file mode 100755 index 00000000000..1e80907a23b --- /dev/null +++ b/.cursor/start.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# Per-boot startup for the Goof Cloud Agent environment. +# Starts the Docker daemon (fuse-overlayfs) and the mongo:3 + mysql:5 database +# containers the app depends on, then waits until they accept connections. +# Idempotent: safe to run repeatedly; it will not create duplicate containers. +set -euo pipefail + +wait_for_port() { + local host="$1" port="$2" name="$3" tries="${4:-60}" + for _ in $(seq 1 "$tries"); do + if (exec 3<>"/dev/tcp/${host}/${port}") 2>/dev/null; then + exec 3>&- 3<&- 2>/dev/null || true + echo "${name} is ready on ${host}:${port}" + return 0 + fi + sleep 1 + done + echo "WARNING: ${name} did not become ready on ${host}:${port}" >&2 + return 1 +} + +# MySQL opens its TCP port while still initializing its data directory on first +# boot, so a bare port check is not enough. Wait until the server can actually +# answer queries before returning, otherwise the app connects too early, fails +# to seed the users table, and crashes on the first /users or /login request. +wait_for_mysql() { + local tries="${1:-120}" + for _ in $(seq 1 "$tries"); do + if sudo docker exec goof-mysql mysqladmin ping -uroot -proot --silent >/dev/null 2>&1; then + echo "MySQL is ready to serve queries" + return 0 + fi + sleep 1 + done + echo "WARNING: MySQL did not become query-ready in time" >&2 + return 1 +} + +# --- Ensure the Docker daemon is running --- +if ! sudo docker info >/dev/null 2>&1; then + sudo rm -f /var/run/docker.pid + sudo setsid dockerd >/tmp/dockerd.log 2>&1 & + for _ in $(seq 1 30); do sudo docker info >/dev/null 2>&1 && break; sleep 1; done +fi + +# --- MongoDB (mongo:3) --- +if ! sudo docker ps --format '{{.Names}}' | grep -qx 'goof-mongo'; then + sudo docker rm -f goof-mongo >/dev/null 2>&1 || true + sudo docker image inspect mongo:3 >/dev/null 2>&1 || sudo docker pull mongo:3 + sudo docker run -d --name goof-mongo -p 27017:27017 mongo:3 >/dev/null +fi + +# --- MySQL (mysql:5, for the TypeORM users table) --- +if ! sudo docker ps --format '{{.Names}}' | grep -qx 'goof-mysql'; then + sudo docker rm -f goof-mysql >/dev/null 2>&1 || true + sudo docker image inspect mysql:5 >/dev/null 2>&1 || sudo docker pull mysql:5 + sudo docker run -d --name goof-mysql \ + -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=acme \ + -p 3306:3306 mysql:5 >/dev/null +fi + +# --- Wait for the databases so the app connects cleanly on first launch --- +wait_for_port 127.0.0.1 27017 "MongoDB" 60 || true +wait_for_port 127.0.0.1 3306 "MySQL (port)" 120 || true +wait_for_mysql 180 || true + +echo "start.sh complete"