diff --git a/Dockerfile b/Dockerfile index a7a3c7f..a12c450 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,14 +46,15 @@ RUN npm run build # ================================ FROM node:20-alpine AS runner -# Install dumb-init for proper signal handling -RUN apk add --no-cache dumb-init +# Install dumb-init for proper signal handling and Docker CLI for restart capability +RUN apk add --no-cache dumb-init docker-cli WORKDIR /app # Create non-root user RUN addgroup --system --gid 1001 nodejs && \ - adduser --system --uid 1001 nextjs + adduser --system --uid 1001 nextjs && \ + addgroup nextjs root # Copy necessary files from builder COPY --from=builder /app/next.config.js ./ diff --git a/README.md b/README.md index 64aa0a4..1293b53 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,24 @@ POST /api/trading/check-risk # Get current settings GET /api/settings +# Update settings +POST /api/settings +{ + "MAX_POSITION_SIZE_USD": 100, + "LEVERAGE": 10, + "STOP_LOSS_PERCENT": -1.5, + ... +} + +# Restart bot container (apply settings) +POST /api/restart +``` + +**Notes:** +- Settings changes require container restart to take effect +- Use the web UI's "Restart Bot" button or call `/api/restart` +- Restart watcher must be running (see setup below) + # Update settings POST /api/settings { @@ -223,6 +241,26 @@ docker compose restart trading-bot docker compose down ``` +### Restart Watcher (Required for Web UI Restart Button) +The restart watcher monitors for restart requests from the web UI: + +```bash +# Start watcher manually +cd /home/icke/traderv4 +nohup ./watch-restart.sh > logs/restart-watcher.log 2>&1 & + +# OR install as systemd service (recommended) +sudo cp trading-bot-restart-watcher.service /etc/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl enable trading-bot-restart-watcher +sudo systemctl start trading-bot-restart-watcher + +# Check watcher status +sudo systemctl status trading-bot-restart-watcher +``` + +The watcher enables the "Restart Bot" button in the web UI to automatically restart the container when settings are changed. + ### Environment Variables All settings are configured via `.env` file: - Drift wallet credentials diff --git a/app/api/restart/route.ts b/app/api/restart/route.ts new file mode 100644 index 0000000..7fdb638 --- /dev/null +++ b/app/api/restart/route.ts @@ -0,0 +1,38 @@ +/** + * Container Restart API Endpoint + * + * Creates a restart flag file that triggers container restart from the host + */ + +import { NextResponse } from 'next/server' +import fs from 'fs' +import path from 'path' + +const RESTART_FLAG = path.join(process.cwd(), 'logs', '.restart-requested') + +export async function POST() { + try { + // Create logs directory if it doesn't exist + const logsDir = path.dirname(RESTART_FLAG) + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }) + } + + // Create restart flag file + fs.writeFileSync(RESTART_FLAG, new Date().toISOString(), 'utf-8') + + return NextResponse.json({ + success: true, + message: 'Restart requested. Container will restart in a few seconds...' + }) + } catch (error: any) { + console.error('Failed to create restart flag:', error) + return NextResponse.json( + { + error: 'Failed to request restart', + details: error.message + }, + { status: 500 } + ) + } +} diff --git a/app/settings/page.tsx b/app/settings/page.tsx index 46230fa..9ce761d 100644 --- a/app/settings/page.tsx +++ b/app/settings/page.tsx @@ -29,6 +29,7 @@ export default function SettingsPage() { const [settings, setSettings] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) + const [restarting, setRestarting] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null) useEffect(() => { @@ -58,7 +59,7 @@ export default function SettingsPage() { }) if (response.ok) { - setMessage({ type: 'success', text: 'Settings saved! Restart the bot to apply changes.' }) + setMessage({ type: 'success', text: 'Settings saved! Click "Restart Bot" to apply changes.' }) } else { setMessage({ type: 'error', text: 'Failed to save settings' }) } @@ -68,6 +69,25 @@ export default function SettingsPage() { setSaving(false) } + const restartBot = async () => { + setRestarting(true) + setMessage(null) + try { + const response = await fetch('/api/restart', { + method: 'POST', + }) + + if (response.ok) { + setMessage({ type: 'success', text: 'Bot is restarting... Settings will be applied in ~10 seconds.' }) + } else { + setMessage({ type: 'error', text: 'Failed to restart bot. Please restart manually with: docker restart trading-bot' }) + } + } catch (error) { + setMessage({ type: 'error', text: 'Failed to restart bot. Please restart manually with: docker restart trading-bot' }) + } + setRestarting(false) + } + const updateSetting = (key: keyof TradingSettings, value: any) => { if (!settings) return setSettings({ ...settings, [key]: value }) @@ -301,7 +321,7 @@ export default function SettingsPage() { - {/* Save Button */} + {/* Action Buttons */}
+
-
- ⚠️ Changes require bot restart to take effect +
+ 💡 Save settings first, then click Restart Bot to apply changes
diff --git a/docker-compose.yml b/docker-compose.yml index b171c96..185643f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,10 +20,10 @@ services: # Load from .env file (create from .env.example) DRIFT_WALLET_PRIVATE_KEY: ${DRIFT_WALLET_PRIVATE_KEY} - DRIFT_ENV: ${DRIFT_ENV:-mainnet-beta} + DRIFT_ENV: "${DRIFT_ENV:-mainnet-beta}" API_SECRET_KEY: ${API_SECRET_KEY} SOLANA_RPC_URL: ${SOLANA_RPC_URL} - PYTH_HERMES_URL: ${PYTH_HERMES_URL:-https://hermes.pyth.network} + PYTH_HERMES_URL: "${PYTH_HERMES_URL:-https://hermes.pyth.network}" # Trading configuration MAX_POSITION_SIZE_USD: ${MAX_POSITION_SIZE_USD:-50} @@ -52,6 +52,9 @@ services: # Mount logs directory - ./logs:/app/logs + # Mount Docker socket for container restart capability + - /var/run/docker.sock:/var/run/docker.sock + # Mount for hot reload in development (comment out in production) # - ./v4:/app/v4:ro diff --git a/trading-bot-restart-watcher.service b/trading-bot-restart-watcher.service new file mode 100644 index 0000000..6600cc6 --- /dev/null +++ b/trading-bot-restart-watcher.service @@ -0,0 +1,17 @@ +[Unit] +Description=Trading Bot v4 Container Restart Watcher +After=docker.service +Requires=docker.service + +[Service] +Type=simple +User=root +WorkingDirectory=/home/icke/traderv4 +ExecStart=/home/icke/traderv4/watch-restart.sh +Restart=always +RestartSec=10 +StandardOutput=append:/home/icke/traderv4/logs/restart-watcher.log +StandardError=append:/home/icke/traderv4/logs/restart-watcher.log + +[Install] +WantedBy=multi-user.target diff --git a/watch-restart.sh b/watch-restart.sh new file mode 100755 index 0000000..c68a44e --- /dev/null +++ b/watch-restart.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# +# Container Restart Watcher +# Monitors for restart flag and restarts the trading-bot container +# + +WATCH_DIR="/home/icke/traderv4/logs" +RESTART_FLAG="$WATCH_DIR/.restart-requested" +CONTAINER_NAME="trading-bot-v4" + +echo "🔍 Watching for restart requests in: $WATCH_DIR" +echo "📦 Container: $CONTAINER_NAME" +echo "" + +# Create logs directory if it doesn't exist +mkdir -p "$WATCH_DIR" + +while true; do + if [ -f "$RESTART_FLAG" ]; then + echo "🔄 Restart requested at $(cat $RESTART_FLAG)" + echo "🔄 Restarting container: $CONTAINER_NAME" + + # Remove flag before restart + rm "$RESTART_FLAG" + + # Restart container + cd /home/icke/traderv4 + docker compose restart $CONTAINER_NAME + + if [ $? -eq 0 ]; then + echo "✅ Container restarted successfully" + else + echo "❌ Failed to restart container" + fi + echo "" + fi + + # Check every 2 seconds + sleep 2 +done