|
|
FROM ubuntu:22.04 |
|
|
|
|
|
# Prevent interactive prompts |
|
|
ENV DEBIAN_FRONTEND=noninteractive |
|
|
|
|
|
# Install dependencies |
|
|
RUN apt-get update && apt-get install -y \ |
|
|
wget \ |
|
|
unzip \ |
|
|
xvfb \ |
|
|
python3 \ |
|
|
python3-pip \ |
|
|
curl \ |
|
|
libgtk-3-0 \ |
|
|
libnotify4 \ |
|
|
libnss3 \ |
|
|
libxss1 \ |
|
|
libxtst6 \ |
|
|
xdg-utils \ |
|
|
libatspi2.0-0 \ |
|
|
libdrm2 \ |
|
|
libgbm1 \ |
|
|
libasound2 \ |
|
|
&& rm -rf /var/lib/apt/lists/* |
|
|
|
|
|
# Install Python packages for keep-alive |
|
|
RUN pip3 install flask requests APScheduler |
|
|
|
|
|
# Set working directory |
|
|
WORKDIR /app |
|
|
|
|
|
# Download and extract FeelingSurf (use x64 version for Hugging Face) |
|
|
RUN wget -q https://github.com/feelingsurf/viewer/releases/download/2.5.0/FeelingSurfViewer-linux-x64-2.5.0.zip && \ |
|
|
unzip -q FeelingSurfViewer-linux-x64-2.5.0.zip && \ |
|
|
rm FeelingSurfViewer-linux-x64-2.5.0.zip && \ |
|
|
chmod +x FeelingSurfViewer |
|
|
|
|
|
# Create enhanced status page with auto-refresh and external ping trigger |
|
|
RUN echo '<!DOCTYPE html>\n\ |
|
|
<html>\n\ |
|
|
<head>\n\ |
|
|
<title>FeelingSurf Viewer - Always Active</title>\n\ |
|
|
<meta http-equiv="refresh" content="30">\n\ |
|
|
<style>\n\ |
|
|
body { font-family: system-ui; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; }\n\ |
|
|
.container { background: white; padding: 40px; border-radius: 20px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); max-width: 700px; }\n\ |
|
|
.status { color: #28a745; font-size: 32px; font-weight: bold; margin-bottom: 20px; }\n\ |
|
|
.pulse { animation: pulse 2s infinite; }\n\ |
|
|
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n\ |
|
|
.info { margin-top: 20px; color: #555; line-height: 1.8; }\n\ |
|
|
.info strong { color: #333; }\n\ |
|
|
.badge { display: inline-block; background: #e7f3ff; color: #0066cc; padding: 4px 12px; border-radius: 12px; font-size: 14px; margin: 5px 5px 5px 0; }\n\ |
|
|
.heartbeat { background: #dcfce7; color: #16a34a; }\n\ |
|
|
.activity { background: #f3f4f6; padding: 15px; border-radius: 10px; margin-top: 20px; font-size: 14px; }\n\ |
|
|
.timestamp { color: #888; font-size: 12px; }\n\ |
|
|
.ping-log { background: #fff9db; padding: 10px; border-radius: 5px; margin-top: 10px; max-height: 150px; overflow-y: auto; font-size: 12px; font-family: monospace; }\n\ |
|
|
.external-ping { background: #e0f2fe; padding: 15px; border-radius: 10px; margin-top: 15px; }\n\ |
|
|
</style>\n\ |
|
|
<script>\n\ |
|
|
let pingCount = 0;\n\ |
|
|
const logDiv = document.getElementById("ping-log");\n\ |
|
|
\n\ |
|
|
function addLog(message) {\n\ |
|
|
if (logDiv) {\n\ |
|
|
const time = new Date().toLocaleTimeString();\n\ |
|
|
logDiv.innerHTML += `[${time}] ${message}<br>`;\n\ |
|
|
logDiv.scrollTop = logDiv.scrollHeight;\n\ |
|
|
}\n\ |
|
|
}\n\ |
|
|
\n\ |
|
|
|
|
|
setInterval(() => {\n\ |
|
|
fetch("/ping")\n\ |
|
|
.then(() => {\n\ |
|
|
pingCount++;\n\ |
|
|
document.getElementById("ping-count").textContent = pingCount;\n\ |
|
|
addLog("β Internal ping successful");\n\ |
|
|
})\n\ |
|
|
.catch(() => addLog("β Internal ping failed"));\n\ |
|
|
}, 25000);\n\ |
|
|
\n\ |
|
|
|
|
|
setInterval(() => {\n\ |
|
|
document.getElementById("time").textContent = new Date().toLocaleTimeString();\n\ |
|
|
}, 1000);\n\ |
|
|
\n\ |
|
|
|
|
|
window.addEventListener("load", () => {\n\ |
|
|
addLog("Page loaded - keep-alive active");\n\ |
|
|
});\n\ |
|
|
</script>\n\ |
|
|
</head>\n\ |
|
|
<body>\n\ |
|
|
<div class="container">\n\ |
|
|
<div class="status pulse">β
FeelingSurf Viewer Active</div>\n\ |
|
|
<div class="info">\n\ |
|
|
<p><strong>Status:</strong> <span class="badge heartbeat">π’ Always Running</span></p>\n\ |
|
|
<p><strong>User:</strong> alllogin</p>\n\ |
|
|
<p><strong>Space URL:</strong> <a href="https://huggingface.co/spaces/huijio/testsurf" target="_blank">huijio/testsurf</a></p>\n\ |
|
|
<p><strong>Mode:</strong> Headless Browser</p>\n\ |
|
|
<p><strong>Version:</strong> 2.5.0</p>\n\ |
|
|
<p><strong>Last Activity:</strong> <span id="time" class="timestamp"></span></p>\n\ |
|
|
<p><strong>Internal Pings:</strong> <span id="ping-count">0</span></p>\n\ |
|
|
<div class="activity">\n\ |
|
|
<strong>π‘οΈ Anti-Sleep Features:</strong><br>\n\ |
|
|
β’ Auto-refresh every 30 seconds<br>\n\ |
|
|
β’ Internal keep-alive pings (25s interval)<br>\n\ |
|
|
β’ External self-ping (2 min interval)<br>\n\ |
|
|
β’ UptimeRobot monitoring ready<br>\n\ |
|
|
β’ Continuous activity logging\n\ |
|
|
</div>\n\ |
|
|
<div class="external-ping">\n\ |
|
|
<strong>π External Monitoring Endpoint:</strong><br>\n\ |
|
|
<code>https://huijio-testsurf.hf.space/health</code><br>\n\ |
|
|
<small>Configure UptimeRobot or similar service to ping this URL every 5 minutes</small>\n\ |
|
|
</div>\n\ |
|
|
<div class="ping-log" id="ping-log"></div>\n\ |
|
|
</div>\n\ |
|
|
</div>\n\ |
|
|
</body>\n\ |
|
|
</html>' > /app/status.html |
|
|
|
|
|
# Create Flask keep-alive server with aggressive anti-sleep |
|
|
RUN echo 'from flask import Flask, send_file, jsonify\n\ |
|
|
from apscheduler.schedulers.background import BackgroundScheduler\n\ |
|
|
import requests\n\ |
|
|
import os\n\ |
|
|
import logging\n\ |
|
|
from datetime import datetime\n\ |
|
|
import threading\n\ |
|
|
import time\n\ |
|
|
\n\ |
|
|
app = Flask(__name__)\n\ |
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")\n\ |
|
|
\n\ |
|
|
# Store last activity time and ping statistics\n\ |
|
|
last_activity = datetime.now()\n\ |
|
|
ping_count = {"internal": 0, "external": 0, "self": 0}\n\ |
|
|
space_url = "https://huijio-testsurf.hf.space"\n\ |
|
|
\n\ |
|
|
@app.route("/")\n\ |
|
|
def index():\n\ |
|
|
global last_activity\n\ |
|
|
last_activity = datetime.now()\n\ |
|
|
ping_count["external"] += 1\n\ |
|
|
external_count = ping_count["external"]\n\ |
|
|
app.logger.info(f"[INDEX] Page accessed - External pings: {external_count}")\n\ |
|
|
return send_file("/app/status.html")\n\ |
|
|
\n\ |
|
|
@app.route("/ping")\n\ |
|
|
def ping():\n\ |
|
|
global last_activity\n\ |
|
|
last_activity = datetime.now()\n\ |
|
|
ping_count["internal"] += 1\n\ |
|
|
return jsonify({\n\ |
|
|
"status": "alive",\n\ |
|
|
"time": str(last_activity),\n\ |
|
|
"ping_count": ping_count\n\ |
|
|
})\n\ |
|
|
\n\ |
|
|
@app.route("/health")\n\ |
|
|
def health():\n\ |
|
|
global last_activity\n\ |
|
|
last_activity = datetime.now()\n\ |
|
|
uptime = datetime.now() - start_time\n\ |
|
|
return jsonify({\n\ |
|
|
"status": "healthy",\n\ |
|
|
"last_activity": str(last_activity),\n\ |
|
|
"uptime_seconds": int(uptime.total_seconds()),\n\ |
|
|
"ping_stats": ping_count,\n\ |
|
|
"space_url": space_url\n\ |
|
|
})\n\ |
|
|
\n\ |
|
|
@app.route("/wake")\n\ |
|
|
def wake():\n\ |
|
|
"""Endpoint specifically for external services to wake the space"""\n\ |
|
|
global last_activity\n\ |
|
|
last_activity = datetime.now()\n\ |
|
|
app.logger.info("[WAKE] External wake call received")\n\ |
|
|
return jsonify({"status": "awake", "time": str(last_activity)})\n\ |
|
|
\n\ |
|
|
# Aggressive self-ping function\n\ |
|
|
def self_ping():\n\ |
|
|
try:\n\ |
|
|
# Ping multiple endpoints to ensure activity\n\ |
|
|
endpoints = ["/ping", "/health"]\n\ |
|
|
for endpoint in endpoints:\n\ |
|
|
try:\n\ |
|
|
response = requests.get(f"http://localhost:7860{endpoint}", timeout=5)\n\ |
|
|
ping_count["self"] += 1\n\ |
|
|
self_count = ping_count["self"]\n\ |
|
|
app.logger.info(f"[SELF-PING] {endpoint} successful - Total self-pings: {self_count}")\n\ |
|
|
except Exception as e:\n\ |
|
|
app.logger.error(f"[SELF-PING] {endpoint} failed: {e}")\n\ |
|
|
except Exception as e:\n\ |
|
|
app.logger.error(f"[SELF-PING] Critical error: {e}")\n\ |
|
|
\n\ |
|
|
# External self-ping (ping the actual Hugging Face URL)\n\ |
|
|
def external_self_ping():\n\ |
|
|
try:\n\ |
|
|
response = requests.get(f"{space_url}/wake", timeout=10)\n\ |
|
|
app.logger.info(f"[EXTERNAL-PING] Space wake successful: {response.status_code}")\n\ |
|
|
except Exception as e:\n\ |
|
|
app.logger.warning(f"[EXTERNAL-PING] Could not reach external URL: {e}")\n\ |
|
|
\n\ |
|
|
# Continuous activity generator\n\ |
|
|
def generate_activity():\n\ |
|
|
"""Generate continuous low-level activity to prevent sleep"""\n\ |
|
|
while True:\n\ |
|
|
try:\n\ |
|
|
# Simple calculation to generate CPU activity\n\ |
|
|
_ = sum(range(1000))\n\ |
|
|
time.sleep(60) # Every minute\n\ |
|
|
except Exception as e:\n\ |
|
|
app.logger.error(f"[ACTIVITY] Error: {e}")\n\ |
|
|
\n\ |
|
|
# Store start time\n\ |
|
|
start_time = datetime.now()\n\ |
|
|
\n\ |
|
|
# Schedule multiple keep-alive mechanisms\n\ |
|
|
scheduler = BackgroundScheduler()\n\ |
|
|
\n\ |
|
|
# Internal self-ping every 1 minute\n\ |
|
|
scheduler.add_job(func=self_ping, trigger="interval", seconds=60, id="self_ping")\n\ |
|
|
\n\ |
|
|
# External self-ping every 3 minutes\n\ |
|
|
scheduler.add_job(func=external_self_ping, trigger="interval", seconds=180, id="external_ping")\n\ |
|
|
\n\ |
|
|
# Log status every 5 minutes\n\ |
|
|
def log_status():\n\ |
|
|
app.logger.info(f"[STATUS] Uptime: {datetime.now() - start_time}, Pings: {ping_count}")\n\ |
|
|
\n\ |
|
|
scheduler.add_job(func=log_status, trigger="interval", seconds=300, id="status_log")\n\ |
|
|
\n\ |
|
|
scheduler.start()\n\ |
|
|
\n\ |
|
|
# Start activity generator in background thread\n\ |
|
|
activity_thread = threading.Thread(target=generate_activity, daemon=True)\n\ |
|
|
activity_thread.start()\n\ |
|
|
\n\ |
|
|
app.logger.info("=" * 50)\n\ |
|
|
app.logger.info("π‘οΈ ANTI-SLEEP PROTECTION ACTIVATED")\n\ |
|
|
app.logger.info(f"π Space URL: {space_url}")\n\ |
|
|
app.logger.info("β Internal self-ping: Every 60 seconds")\n\ |
|
|
app.logger.info("β External self-ping: Every 3 minutes")\n\ |
|
|
app.logger.info("β Activity generator: Running")\n\ |
|
|
app.logger.info("β Status logging: Every 5 minutes")\n\ |
|
|
app.logger.info("=" * 50)\n\ |
|
|
\n\ |
|
|
if __name__ == "__main__":\n\ |
|
|
app.run(host="0.0.0.0", port=7860)' > /app/keep_alive_server.py |
|
|
|
|
|
# Create startup script with enhanced monitoring |
|
|
RUN echo '#!/bin/bash\n\ |
|
|
echo "π Starting FeelingSurf Viewer with Anti-Sleep Protection..."\n\ |
|
|
echo "================================================"\n\ |
|
|
\n\ |
|
|
# Get current Hugging Face IP address\n\ |
|
|
echo "=== Current Hugging Face Space Information ==="\n\ |
|
|
CURRENT_IP=$(curl -s -H "Accept: application/json" https://api.ipify.org?format=json | grep -oE "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}")\n\ |
|
|
if [ -n "$CURRENT_IP" ]; then\n\ |
|
|
echo "π Current Public IP: $CURRENT_IP"\n\ |
|
|
echo "π Hugging Face Space IP: $CURRENT_IP"\n\ |
|
|
else\n\ |
|
|
echo "β οΈ Could not determine current IP address"\n\ |
|
|
fi\n\ |
|
|
echo "π Space URL: https://huijio-testsurf.hf.space"\n\ |
|
|
echo "π Health Check: https://huijio-testsurf.hf.space/health"\n\ |
|
|
echo "============================================="\n\ |
|
|
echo ""\n\ |
|
|
\n\ |
|
|
# Start Xvfb (virtual display)\n\ |
|
|
echo "π₯οΈ Starting virtual display..."\n\ |
|
|
Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &\n\ |
|
|
export DISPLAY=:99\n\ |
|
|
sleep 2\n\ |
|
|
\n\ |
|
|
# Start FeelingSurf in background\n\ |
|
|
echo "π Starting FeelingSurf Viewer..."\n\ |
|
|
cd /app\n\ |
|
|
./FeelingSurfViewer --access-token d6e659ba6b59c9866fba8ff01bc56e04 --no-sandbox 2>&1 | tee /tmp/viewer.log &\n\ |
|
|
sleep 5\n\ |
|
|
\n\ |
|
|
# Start activity monitor\n\ |
|
|
echo "π Starting activity monitor..."\n\ |
|
|
(\n\ |
|
|
while true; do\n\ |
|
|
echo "[$(date)] β System active - Memory: $(free -h | grep Mem | awk '\''{print $3}'\'' ) CPU Load: $(uptime | awk -F'\''load average:'\'' '\''{ print $2 }'\'' | cut -d, -f1)"\n\ |
|
|
sleep 300 # Log every 5 minutes\n\ |
|
|
done\n\ |
|
|
) &\n\ |
|
|
\n\ |
|
|
# Start Flask keep-alive server\n\ |
|
|
echo "π‘οΈ Starting Flask keep-alive server on port 7860..."\n\ |
|
|
echo ""\n\ |
|
|
echo "=== Anti-Sleep Protection Active ==="\n\ |
|
|
echo "β Page auto-refresh: 30 seconds"\n\ |
|
|
echo "β Internal self-ping: 60 seconds"\n\ |
|
|
echo "β External self-ping: 3 minutes"\n\ |
|
|
echo "β Activity generator: Running"\n\ |
|
|
echo "β Health monitoring active"\n\ |
|
|
echo "==================================="\n\ |
|
|
echo ""\n\ |
|
|
echo "=== Setup External Monitoring (Recommended) ==="\n\ |
|
|
echo "1. Visit: https://uptimerobot.com (Free tier available)"\n\ |
|
|
echo "2. Add HTTP(s) monitor:"\n\ |
|
|
echo " URL: https://huijio-testsurf.hf.space/health"\n\ |
|
|
echo " Interval: 5 minutes"\n\ |
|
|
echo "3. Alternative services: Uptime Kuma, Better Uptime, Pingdom"\n\ |
|
|
echo "==============================================="\n\ |
|
|
echo ""\n\ |
|
|
echo "=== FeelingSurf Viewer Logs ==="\n\ |
|
|
tail -f /tmp/viewer.log &\n\ |
|
|
\n\ |
|
|
# Start Flask server (this keeps the container running)\n\ |
|
|
python3 /app/keep_alive_server.py' > /app/start.sh && \ |
|
|
chmod +x /app/start.sh |
|
|
|
|
|
# Set environment |
|
|
ENV access_token="d6e659ba6b59c9866fba8ff01bc56e04" |
|
|
ENV DISPLAY=:99 |
|
|
ENV PYTHONUNBUFFERED=1 |
|
|
|
|
|
EXPOSE 7860 |
|
|
|
|
|
CMD ["/app/start.sh"] |