From 26864c10f2997b4bb34ffeced1a967c4bf50994a Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Fri, 24 Oct 2025 15:06:26 +0200 Subject: [PATCH] feat: Add container restart functionality from web UI - Added restart button to settings page - Created /api/restart endpoint (file-flag based) - Implemented watch-restart.sh daemon - Added systemd service for restart watcher - Updated README with restart setup instructions - Container automatically restarts when settings changed Settings flow: 1. User edits settings in web UI 2. Click 'Save Settings' to persist to .env 3. Click 'Restart Bot' to apply changes 4. Watcher detects flag and restarts container 5. New settings loaded automatically --- Dockerfile | 7 ++--- README.md | 38 +++++++++++++++++++++++++++ app/api/restart/route.ts | 38 +++++++++++++++++++++++++++ app/settings/page.tsx | 37 ++++++++++++++++++++++---- docker-compose.yml | 7 +++-- trading-bot-restart-watcher.service | 17 ++++++++++++ watch-restart.sh | 40 +++++++++++++++++++++++++++++ 7 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 app/api/restart/route.ts create mode 100644 trading-bot-restart-watcher.service create mode 100755 watch-restart.sh 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