| 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"] |