FROM node:20-bookworm-slim ENV NODE_ENV=production \ PORT=7860 \ HOSTNAME=0.0.0.0 \ NEXT_PUBLIC_STORAGE_TYPE=kvrocks \ KVROCKS_URL=redis://127.0.0.1:6666 \ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 \ TZ=Asia/Shanghai RUN apt-get update && \ apt-get install -y --no-install-recommends \ libjemalloc2 \ openssl \ ca-certificates \ libatomic1 \ python3 \ python3-pip \ python3-requests \ curl \ tar \ && rm -rf /var/lib/apt/lists/* RUN pip3 install webdavclient3 --break-system-packages && \ rm -rf /root/.cache/pip COPY --from=apache/kvrocks:latest /bin/kvrocks /usr/local/bin/kvrocks COPY --from=ghcr.io/decohererk/decotv:latest /app /app WORKDIR /app RUN echo "import os" > /backup_utils.py && \ echo "import sys" >> /backup_utils.py && \ echo "import tarfile" >> /backup_utils.py && \ echo "import shutil" >> /backup_utils.py && \ echo "import time" >> /backup_utils.py && \ echo "import urllib3" >> /backup_utils.py && \ echo "from webdav3.client import Client" >> /backup_utils.py && \ echo "import requests" >> /backup_utils.py && \ echo "" >> /backup_utils.py && \ echo "urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)" >> /backup_utils.py && \ echo "webdav_url = os.environ.get('WEBDAV_URL')" >> /backup_utils.py && \ echo "webdav_user = os.environ.get('WEBDAV_USERNAME')" >> /backup_utils.py && \ echo "webdav_password = os.environ.get('WEBDAV_PASSWORD')" >> /backup_utils.py && \ echo "backup_path = os.environ.get('WEBDAV_BACKUP_PATH', '')" >> /backup_utils.py && \ echo "data_dir = '/data/kvrocks'" >> /backup_utils.py && \ echo "" >> /backup_utils.py && \ echo "if not webdav_url or not webdav_user or not webdav_password:" >> /backup_utils.py && \ echo " sys.exit(0)" >> /backup_utils.py && \ echo "" >> /backup_utils.py && \ echo "full_url = webdav_url" >> /backup_utils.py && \ echo "if backup_path:" >> /backup_utils.py && \ echo " full_url = webdav_url.rstrip('/') + '/' + backup_path.strip('/')" >> /backup_utils.py && \ echo "" >> /backup_utils.py && \ echo "options = {" >> /backup_utils.py && \ echo " 'webdav_hostname': full_url," >> /backup_utils.py && \ echo " 'webdav_login': webdav_user," >> /backup_utils.py && \ echo " 'webdav_password': webdav_password," >> /backup_utils.py && \ echo " 'disable_check_certificate_hostname_match': True" >> /backup_utils.py && \ echo "}" >> /backup_utils.py && \ echo "client = Client(options)" >> /backup_utils.py && \ echo "auth_tuple = (webdav_user, webdav_password)" >> /backup_utils.py && \ echo "" >> /backup_utils.py && \ echo "def restore():" >> /backup_utils.py && \ echo " try:" >> /backup_utils.py && \ echo " files = client.list()" >> /backup_utils.py && \ echo " backups = [f for f in files if f.endswith('.tar.gz') and f.startswith('decotv_backup_')]" >> /backup_utils.py && \ echo " if not backups:" >> /backup_utils.py && \ echo " return" >> /backup_utils.py && \ echo " latest_backup = sorted(backups)[-1]" >> /backup_utils.py && \ echo " local_tmp = '/tmp/' + latest_backup" >> /backup_utils.py && \ echo " download_url = full_url.rstrip('/') + '/' + latest_backup" >> /backup_utils.py && \ echo " with requests.get(download_url, auth=auth_tuple, stream=True, verify=False) as r:" >> /backup_utils.py && \ echo " r.raise_for_status()" >> /backup_utils.py && \ echo " with open(local_tmp, 'wb') as f:" >> /backup_utils.py && \ echo " for chunk in r.iter_content(chunk_size=8192):" >> /backup_utils.py && \ echo " f.write(chunk)" >> /backup_utils.py && \ echo " if os.path.exists(data_dir):" >> /backup_utils.py && \ echo " shutil.rmtree(data_dir)" >> /backup_utils.py && \ echo " os.makedirs(data_dir, exist_ok=True)" >> /backup_utils.py && \ echo " with tarfile.open(local_tmp, 'r:gz') as tar:" >> /backup_utils.py && \ echo " tar.extractall(path='/data')" >> /backup_utils.py && \ echo " os.remove(local_tmp)" >> /backup_utils.py && \ echo " except Exception as e:" >> /backup_utils.py && \ echo " print(e)" >> /backup_utils.py && \ echo "" >> /backup_utils.py && \ echo "def backup():" >> /backup_utils.py && \ echo " timestamp = time.strftime('%Y%m%d_%H%M%S')" >> /backup_utils.py && \ echo " filename = 'decotv_backup_' + timestamp + '.tar.gz'" >> /backup_utils.py && \ echo " local_path = '/tmp/' + filename" >> /backup_utils.py && \ echo " try:" >> /backup_utils.py && \ echo " with tarfile.open(local_path, 'w:gz') as tar:" >> /backup_utils.py && \ echo " tar.add(data_dir, arcname='kvrocks')" >> /backup_utils.py && \ echo " client.upload_sync(remote_path=filename, local_path=local_path)" >> /backup_utils.py && \ echo " os.remove(local_path)" >> /backup_utils.py && \ echo " files = client.list()" >> /backup_utils.py && \ echo " backups = [f for f in files if f.endswith('.tar.gz') and f.startswith('decotv_backup_')]" >> /backup_utils.py && \ echo " backups.sort()" >> /backup_utils.py && \ echo " if len(backups) > 5:" >> /backup_utils.py && \ echo " for f in backups[:-5]:" >> /backup_utils.py && \ echo " client.clean(f)" >> /backup_utils.py && \ echo " except Exception as e:" >> /backup_utils.py && \ echo " print(e)" >> /backup_utils.py && \ echo "" >> /backup_utils.py && \ echo "if __name__ == '__main__':" >> /backup_utils.py && \ echo " action = sys.argv[1]" >> /backup_utils.py && \ echo " if action == 'restore':" >> /backup_utils.py && \ echo " restore()" >> /backup_utils.py && \ echo " elif action == 'backup':" >> /backup_utils.py && \ echo " backup()" >> /backup_utils.py RUN echo '#!/bin/bash' > /start.sh && \ echo 'set -e' >> /start.sh && \ echo 'python3 /backup_utils.py restore' >> /start.sh && \ echo 'mkdir -p /data/kvrocks' >> /start.sh && \ echo 'echo "bind 127.0.0.1" > /data/kvrocks.conf' >> /start.sh && \ echo 'echo "port 6666" >> /data/kvrocks.conf' >> /start.sh && \ echo 'echo "dir /data/kvrocks" >> /data/kvrocks.conf' >> /start.sh && \ echo 'echo "workers 2" >> /data/kvrocks.conf' >> /start.sh && \ echo 'echo "rocksdb.max_background_jobs 2" >> /data/kvrocks.conf' >> /start.sh && \ echo '/usr/local/bin/kvrocks -c /data/kvrocks.conf --daemonize yes' >> /start.sh && \ echo 'sleep 5' >> /start.sh && \ echo '(' >> /start.sh && \ echo ' while true; do' >> /start.sh && \ echo ' SYNC_INTERVAL=${SYNC_INTERVAL:-600}' >> /start.sh && \ echo ' sleep $SYNC_INTERVAL' >> /start.sh && \ echo ' python3 /backup_utils.py backup' >> /start.sh && \ echo ' done' >> /start.sh && \ echo ') &' >> /start.sh && \ echo 'cd /app' >> /start.sh && \ echo 'node start.js' >> /start.sh && \ chmod +x /start.sh && \ mkdir -p /data && \ chmod -R 777 /app /data CMD ["/start.sh"]