diff --git a/stack_orchestrator/data/compose/docker-compose-trashscan-explorer.yml b/stack_orchestrator/data/compose/docker-compose-trashscan-explorer.yml new file mode 100644 index 00000000..556426cd --- /dev/null +++ b/stack_orchestrator/data/compose/docker-compose-trashscan-explorer.yml @@ -0,0 +1,47 @@ +services: + trashscan-db: + image: postgres:14-alpine + restart: unless-stopped + environment: + POSTGRES_USER: ${TRASHSCAN_DB_USER:-trashscan} + POSTGRES_PASSWORD: ${TRASHSCAN_DB_PASSWORD:-password} + POSTGRES_DB: ${TRASHSCAN_DB_NAME:-trashscan} + POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" + volumes: + - trashscan_db_data:/var/lib/postgresql/data + ports: + - "5432" + healthcheck: + test: ["CMD", "pg_isready", "-U", "trashscan"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 5s + + trashscan-explorer: + image: cerc/trashscan-explorer:local + restart: unless-stopped + depends_on: + trashscan-db: + condition: service_healthy + environment: + NODE_ENV: production + DATABASE_URL: ${DATABASE_URL:-postgres://trashscan:password@trashscan-db:5432/trashscan} + PORT: ${TRASHSCAN_PORT:-5000} + SESSION_SECRET: ${SESSION_SECRET:-change-me-in-production} + RPC_URL: ${RPC_URL:-https://rpc.trashscan.io/} + RUN_MIGRATIONS: ${RUN_MIGRATIONS:-true} + CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG} + ports: + - "${TRASHSCAN_HOST_PORT:-5001}:5000" + healthcheck: + test: ["CMD", "nc", "-z", "localhost", "5000"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 30s + extra_hosts: + - "host.docker.internal:host-gateway" + +volumes: + trashscan_db_data: diff --git a/stack_orchestrator/data/container-build/cerc-trashscan-explorer/Dockerfile b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/Dockerfile new file mode 100644 index 00000000..c0cf799e --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/Dockerfile @@ -0,0 +1,10 @@ +FROM cerc/trashscan-explorer-base:local + +COPY ./scripts/start-explorer.sh /scripts/ + +WORKDIR /app + +HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ + CMD nc -z localhost 5000 || exit 1 + +CMD ["/scripts/start-explorer.sh"] diff --git a/stack_orchestrator/data/container-build/cerc-trashscan-explorer/Dockerfile.base b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/Dockerfile.base new file mode 100644 index 00000000..65912543 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/Dockerfile.base @@ -0,0 +1,35 @@ +# Multi-stage build for TrashScan Explorer - Base image +# Stage 1: Build +FROM node:20-bullseye-slim AS builder + +RUN apt-get update && apt-get install -y \ + python3 \ + make \ + g++ \ + git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +# Stage 2: Production runtime +FROM node:20-bullseye-slim + +RUN apt-get update && apt-get install -y \ + netcat-openbsd \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY --from=builder /app/package*.json ./ +# Install production deps + vite (needed for server runtime even in prod due to module import structure) +RUN npm ci --omit=dev && npm install vite + +COPY --from=builder /app/dist ./dist + +EXPOSE 5000 diff --git a/stack_orchestrator/data/container-build/cerc-trashscan-explorer/build.sh b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/build.sh new file mode 100755 index 00000000..48ac203a --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/build.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Build the TrashScan Explorer image +source ${CERC_CONTAINER_BASE_DIR}/build-base.sh + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +# Two-stage build: base image from repo, final image with local scripts +docker build -t cerc/trashscan-explorer-base:local \ + ${build_command_args} \ + -f ${SCRIPT_DIR}/Dockerfile.base \ + ${CERC_REPO_BASE_DIR}/TrashScan-Explorer + +if [[ $? -ne 0 ]]; then + echo "FATAL: Base container build failed, exiting" + exit 1 +fi + +docker build -t cerc/trashscan-explorer:local \ + ${build_command_args} \ + -f ${SCRIPT_DIR}/Dockerfile \ + ${SCRIPT_DIR} diff --git a/stack_orchestrator/data/container-build/cerc-trashscan-explorer/scripts/start-explorer.sh b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/scripts/start-explorer.sh new file mode 100755 index 00000000..ce1be740 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-trashscan-explorer/scripts/start-explorer.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -e + +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +echo "TrashScan Explorer starting..." + +# Wait for database to be ready +if [ -n "$DATABASE_URL" ]; then + echo "Waiting for database to be ready..." + + # Parse DATABASE_URL: postgres://user:pass@host:port/db + DB_HOST=$(echo $DATABASE_URL | sed -e 's|.*@||' -e 's|:.*||' -e 's|/.*||') + DB_PORT=$(echo $DATABASE_URL | sed -e 's|.*@[^:]*:||' -e 's|/.*||') + + if [ -z "$DB_PORT" ] || [ "$DB_PORT" = "$DB_HOST" ]; then + DB_PORT=5432 + fi + + timeout=60 + counter=0 + until nc -z "$DB_HOST" "$DB_PORT" 2>/dev/null; do + counter=$((counter + 1)) + if [ $counter -ge $timeout ]; then + echo "Error: Database not available after ${timeout} seconds" + exit 1 + fi + echo "Waiting for database at ${DB_HOST}:${DB_PORT}... ($counter/$timeout)" + sleep 1 + done + echo "Database is available!" +fi + +# Run database migrations if needed +if [ "${RUN_MIGRATIONS:-true}" = "true" ]; then + echo "Running database migrations..." + npm run db:push 2>/dev/null || echo "No migration script found or migration failed, continuing..." +fi + +# Start the application +echo "Starting TrashScan Explorer on port ${PORT:-5000}..." +exec node dist/index.js diff --git a/stack_orchestrator/data/container-image-list.txt b/stack_orchestrator/data/container-image-list.txt index fd295be5..393dc1f0 100644 --- a/stack_orchestrator/data/container-image-list.txt +++ b/stack_orchestrator/data/container-image-list.txt @@ -60,3 +60,4 @@ cerc/nitro-rpc-client cerc/watcher-merkl-sushiswap-v3 cerc/watcher-sushiswap-v3 cerc/uniswap-interface +cerc/trashscan-explorer diff --git a/stack_orchestrator/data/pod-list.txt b/stack_orchestrator/data/pod-list.txt index 9ad000c7..6ccbe85a 100644 --- a/stack_orchestrator/data/pod-list.txt +++ b/stack_orchestrator/data/pod-list.txt @@ -45,3 +45,4 @@ ponder ipld-eth-server-payments merkl-sushiswap-v3 sushiswap-v3 +trashscan-explorer diff --git a/stack_orchestrator/data/repository-list.txt b/stack_orchestrator/data/repository-list.txt index dba0ed74..32cd31d4 100644 --- a/stack_orchestrator/data/repository-list.txt +++ b/stack_orchestrator/data/repository-list.txt @@ -50,3 +50,4 @@ github.com/cerc-io/ponder github.com/cerc-io/merkl-sushiswap-v3-watcher-ts github.com/cerc-io/sushiswap-v3-watcher-ts github.com/cerc-io/uniswap-interface +github.com/gorbagana-dev/TrashScan-Explorer diff --git a/stack_orchestrator/data/stacks/trashscan-explorer/README.md b/stack_orchestrator/data/stacks/trashscan-explorer/README.md new file mode 100644 index 00000000..0e870db8 --- /dev/null +++ b/stack_orchestrator/data/stacks/trashscan-explorer/README.md @@ -0,0 +1,99 @@ +# TrashScan Explorer Stack + +TrashScan is a blockchain explorer for Gorbagana mainnet (Solana fork). + +## Quick Start + +```bash +# 1. Setup repositories (clones TrashScan-Explorer to ~/cerc/) +laconic-so --stack trashscan-explorer setup-repositories + +# 2. Build containers +laconic-so --stack trashscan-explorer build-containers + +# 3. Deploy +laconic-so --stack trashscan-explorer deploy-system up + +# 4. Verify +docker ps --filter "name=trashscan" +curl http://localhost:5001/ + +# 5. View logs +laconic-so --stack trashscan-explorer deploy-system logs -f + +# 6. Stop +laconic-so --stack trashscan-explorer deploy-system down +``` + +## Access + +After deployment, access the explorer at: **http://localhost:5001** + +Note: Default port is 5001 to avoid conflict with macOS AirPlay Receiver on port 5000. + +## Components + +| Service | Image | Port | Description | +|---------|-------|------|-------------| +| trashscan-explorer | cerc/trashscan-explorer:local | 5001 | React/Express blockchain explorer | +| trashscan-db | postgres:14-alpine | (internal) | PostgreSQL database | + +## Configuration + +Environment variables can be set in your deployment configuration: + +| Variable | Default | Description | +|----------|---------|-------------| +| NODE_ENV | production | Node environment | +| DATABASE_URL | postgres://trashscan:password@trashscan-db:5432/trashscan | Database connection string | +| TRASHSCAN_HOST_PORT | 5001 | Host port for explorer | +| SESSION_SECRET | change-me-in-production | Express session secret | +| RPC_URL | https://rpc.trashscan.io/ | Gorbagana RPC endpoint | +| RUN_MIGRATIONS | true | Run database migrations on startup | + +## External Dependencies + +The explorer connects to the Gorbagana RPC at https://rpc.trashscan.io/ by default. + +## Files in This Stack + +``` +stack-orchestrator/stack_orchestrator/data/ +├── stacks/trashscan-explorer/ +│ ├── stack.yml # Stack definition +│ └── README.md # This file +├── container-build/cerc-trashscan-explorer/ +│ ├── Dockerfile.base # Multi-stage build (base) +│ ├── Dockerfile # Final image with scripts +│ ├── build.sh # Build script +│ └── scripts/ +│ └── start-explorer.sh # Container startup script +└── compose/ + └── docker-compose-trashscan-explorer.yml # Docker Compose +``` + +## Verification Checklist + +After deployment, verify: + +- [ ] `docker ps` shows both containers as `(healthy)` +- [ ] `curl http://localhost:5001/` returns HTTP 200 +- [ ] Logs show "TrashScan Explorer starting..." and "Database is available!" + +## Troubleshooting + +### Port 5000 conflict (macOS) +macOS AirPlay Receiver uses port 5000. This stack defaults to 5001. +To use a different port: `export TRASHSCAN_HOST_PORT=8080` + +### Missing assets error during build +The upstream TrashScan-Explorer repo may be missing the `attached_assets/` directory. +Create placeholder images if needed: +```bash +cd ~/cerc/TrashScan-Explorer +mkdir -p attached_assets +# Create placeholder images for any missing assets +``` + +### "Cannot find package 'vite'" error +The Dockerfile.base includes vite in production deps to handle this. diff --git a/stack_orchestrator/data/stacks/trashscan-explorer/stack.yml b/stack_orchestrator/data/stacks/trashscan-explorer/stack.yml new file mode 100644 index 00000000..f10b3ba3 --- /dev/null +++ b/stack_orchestrator/data/stacks/trashscan-explorer/stack.yml @@ -0,0 +1,9 @@ +version: "1.0" +name: trashscan-explorer +description: "TrashScan blockchain explorer for Gorbagana mainnet" +repos: + - github.com/gorbagana-dev/TrashScan-Explorer +containers: + - cerc/trashscan-explorer +pods: + - trashscan-explorer