diff --git a/.env.example b/.env.example index 94452f5..1f4e6d5 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,11 @@ LOGIN_DESKTOP_WEB_PORT=8788 PROXY_HTTP_PORT=7890 PROXY_CONTROLLER_PORT=9090 +# Used by deploy/install-server.sh when it creates state/cron/root for the first time. +# Format: HH:00-HH:00/Nm. Example below runs every 20 minutes from 10:00 to 17:59, +# runs one final regular send at 18:00, then an unsent fallback at 18:20. +DEFAULT_SCHEDULE=10:00-18:00/20m + # Optional: paste a Mihomo/Clash subscription URL here, then run ./refresh_proxy.sh. PROXY_SUB_URL= PROXY_USER_AGENT=clash-verge/1.7.7 diff --git a/README.md b/README.md index c338c77..4bfbccf 100644 --- a/README.md +++ b/README.md @@ -1,100 +1,102 @@ -# 🔥 douyin-sparkflow +# Douyin SparkFlow -Douyin SparkFlow 提供一个面向个人自用场景的抖音火花维护工具。项目包含 Web 管理面板、登录桌面、定时任务和代理配置模板,默认通过 Docker Compose 部署。 +Douyin SparkFlow 是一个个人自用的抖音火花维护工具,包含 Web 管理后台、扫码登录桌面、定时发送任务和代理配置模板。项目默认使用 Docker Compose 部署。 -## 🧭 上游项目 +本项目基于 [2061360308/DouYinSparkFlow](https://github.com/2061360308/DouYinSparkFlow) 二次开发,补充了 Web 管理、Docker 部署、登录桌面、发送可靠性和运行维护能力。 -本项目基于 [2061360308/DouYinSparkFlow](https://github.com/2061360308/DouYinSparkFlow) 进行二次开发,主要补充 Web 管理、Docker Compose 部署和运行维护相关能力。 +## 功能 -## ✨ 功能特性 +- Web 面板管理账号、目标好友、发送窗口和日志。 +- 浏览器扫码登录并保存登录状态。 +- 按每日时间窗口自动发送,也支持手动补发未成功或未发送目标。 +- 通过 Mihomo/Clash 代理配置访问网络。 +- 登录桌面、Web 后台、定时器、任务容器和代理容器统一编排。 +- 发送确认、好友列表扫描、账号级临时故障冷却等可靠性保护。 -- 🖥️ Web 面板管理账号、目标好友和发送窗口。 -- 🔐 浏览器扫码登录并保存本地登录态。 -- ⏱️ 定时执行续火花任务,支持手动触发和日志查看。 -- 🌐 可选 Mihomo/Clash 代理订阅配置。 -- 🐳 Docker Compose 编排 Web、登录桌面、定时器、任务和代理服务。 -- 🌗 支持小太阳/小月亮切换白天模式和黑夜模式。 - -## 🖼️ 界面预览 - -![Douyin SparkFlow 控制台界面预览](docs/images/ui-preview.png) - -> 📘 **截图教程:** 新版 Web 面板将概览、登录工作区、账号与目标、运行与系统拆成独立视图。按截图完成首次配置,请直接阅读 [Douyin SparkFlow 使用教程](docs/usage.md)。 - -## ⚠️ 免责声明 +## 免责声明 本项目仅用于个人学习、研究和自用场景,不是抖音、字节跳动或相关平台的官方工具,也未获得其授权、背书或关联。 -使用本项目时,请遵守所在地法律法规、抖音平台规则和相关服务协议。请勿将本项目用于商业用途、批量营销、骚扰他人、刷量引流、规避平台风控,或任何可能损害平台、他人账号及第三方权益的行为。 +使用本项目时,请遵守所在地法律法规、平台规则和相关服务协议。请勿用于商业营销、批量骚扰、刷量引流、规避平台风控,或任何可能损害平台、他人账号及第三方权益的行为。 -本项目会在使用者授权登录后,按照使用者配置的账号、好友和发送窗口执行自动化操作。由此产生的账号异常、限流、封禁、数据丢失、服务中断、消息误发、隐私泄露或其他损失,均由使用者自行承担。项目作者和贡献者不对使用本项目产生的任何直接或间接后果负责。 +请妥善保管 `.env`、登录状态、Cookie、代理配置、运行日志和账号数据,不要提交到公开仓库,也不要分享给不可信第三方。继续部署、运行或修改本项目,即表示你理解并接受以上说明。 -请妥善保管 `.env`、登录态、Cookie、代理配置和运行日志等敏感信息,不要提交到公开仓库,也不要分享给不可信的第三方。如果平台规则、接口页面或风控策略发生变化,请立即停止使用并自行评估风险。 +## 服务器一键安装 -继续部署、运行或修改本项目,即表示你已理解并接受以上说明;如果你不同意,请停止使用本项目。 - -## 🚀 快速开始 - -在服务器上推荐使用这条一键安装命令,适合 `raw.githubusercontent.com` 访问较慢或超时的环境: +推荐在服务器上使用 Git clone 方式安装,适合 `raw.githubusercontent.com` 访问较慢的环境: ```bash apt update && apt install -y git curl gnupg ca-certificates && rm -rf /opt/douyin-sparkflow && git clone --depth=1 https://github.com/halfwaystudent/douyin-sparkflow.git /opt/douyin-sparkflow && cd /opt/douyin-sparkflow && bash deploy/install-server.sh ``` -如果 GitHub 克隆时出现 `GnuTLS recv error`,先切到 HTTP/1.1 后重试: - -```bash -git config --global http.version HTTP/1.1 -git config --global http.lowSpeedLimit 1000 -git config --global http.lowSpeedTime 60 -``` - 指定安装目录或代理订阅: ```bash APP_ROOT=/opt/douyin-sparkflow PROXY_SUB_URL='你的 Mihomo/Clash 订阅链接' bash -c 'apt update && apt install -y git curl gnupg ca-certificates && rm -rf "$APP_ROOT" && git clone --depth=1 https://github.com/halfwaystudent/douyin-sparkflow.git "$APP_ROOT" && cd "$APP_ROOT" && bash deploy/install-server.sh' ``` -如果你的服务器可以稳定访问 `raw.githubusercontent.com`,也可以使用精简版: +如果服务器能稳定访问 raw GitHub,也可以使用: ```bash curl -fsSL https://raw.githubusercontent.com/halfwaystudent/douyin-sparkflow/main/deploy/install-server.sh | bash ``` -部署完成后检查容器状态: +常用环境变量: + +```bash +APP_ROOT=/opt/douyin-sparkflow +BRANCH=main +WEB_PORT=8787 +LOGIN_DESKTOP_WEB_PORT=8788 +PROXY_SUB_URL='你的 Mihomo/Clash 订阅链接' +DEFAULT_SCHEDULE='10:00-18:00/20m' +``` + +安装完成后检查容器: ```bash cd /opt/douyin-sparkflow docker compose ps ``` -正常状态应包含: +正常应看到: -- `douyin-web`:`Up`,Web 面板服务。 -- `login-desktop`:`Up`,扫码登录桌面。 -- `douyin-scheduler`:`Up`,定时发送和自动兜底。 -- `mihomo`:`Up`,代理服务。 -- `douyin-task`:`Exited` 或 `Stopped` 是正常的,它是一次性手动任务容器,不需要常驻。 +- `douyin-web`:Web 管理后台。 +- `login-desktop`:扫码登录桌面。 +- `douyin-scheduler`:读取 `state/cron/root` 的定时器。 +- `mihomo`:代理服务。 +- `douyin-task`:一次性任务容器,未常驻是正常的。 -确认 `douyin-web` 已启动后访问: +访问地址: - Web 面板:`http://服务器IP:8787` - 登录桌面:Web 面板中的“登录桌面”入口,默认端口 `8788` -如果外网打不开 Web 面板,请在云服务器安全组、轻量云防火墙或 1Panel 防火墙中放行: +如果外网打不开,请在云服务器安全组、系统防火墙或 1Panel 防火墙中放行: -- `8787/tcp`:Web 管理面板。 +- `8787/tcp`:Web 管理后台。 - `8788/tcp`:扫码登录桌面。 -首次使用: +## 无损更新 -1. 创建管理员账号。 -2. 打开登录工作区并扫码登录抖音。 -3. 保存登录态。 -4. 刷新好友列表并选择目标好友。 -5. 设置发送窗口,例如 `10:00-18:00/10m`。 +更新代码和容器,但保留运行数据: -## 💻 本地运行 +```bash +cd /opt/douyin-sparkflow +ACTION=update bash deploy/install-server.sh +``` + +更新不会覆盖这些运行态文件: + +- `.env` +- `state/` +- `proxy/config.yaml` +- `DouYinSparkFlow/logs/` +- `DouYinSparkFlow/usersData.json` +- `DouYinSparkFlow/config.json` +- `DouYinSparkFlow/webui_settings.json` + +## 本地运行 Windows 需要先启动 Docker Desktop: @@ -102,7 +104,7 @@ Windows 需要先启动 Docker Desktop: .\deploy\install-local.ps1 ``` -如需配置代理订阅: +带代理订阅: ```powershell .\deploy\install-local.ps1 -ProxySubUrl "你的 Mihomo/Clash 订阅链接" @@ -114,19 +116,33 @@ Linux/macOS: ./deploy/install-local.sh ``` -本地脚本会创建 `.env`、运行态目录和默认代理配置,并执行 `docker compose up -d --build`。 +本地脚本会创建 `.env`、`state/cron/root`、`state/login-profile`、`proxy/config.yaml` 和 `DouYinSparkFlow/logs`,然后执行 `docker compose up -d --build`。 -## ⚙️ 配置说明 +## 配置 + +`.env.example` 是部署模板。首次安装时会复制为 `.env`。常用配置: + +```bash +WEB_PORT=8787 +LOGIN_DESKTOP_WEB_PORT=8788 +PROXY_SUB_URL= +DEFAULT_SCHEDULE=10:00-18:00/20m +PLAYWRIGHT_BASE_IMAGE=swr.cn-north-4.myhuaweicloud.com/ddn-k8s/mcr.microsoft.com/playwright/python:v1.56.0-jammy +``` 如果 `.env` 中的 `PROXY_SUB_URL` 为空,项目会使用 `proxy/config.example.yaml` 生成直连代理配置。 -首次构建需要拉取 Playwright 基础镜像。默认 `.env.example` 使用国内同步源;如果同步源不可用,可以将 `.env` 中的 `PLAYWRIGHT_BASE_IMAGE` 改为: +默认定时任务写入 `state/cron/root`: -```bash -PLAYWRIGHT_BASE_IMAGE=mcr.microsoft.com/playwright/python:v1.56.0-jammy +```cron +*/20 10-17 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1 +0 18 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1 +20 18 * * * cd /app && env SPARKFLOW_MANUAL_RUN=1 SPARKFLOW_MANUAL_UNSENT_ONLY=1 PYTHONUNBUFFERED=1 python main.py --doTask >> /app/logs/app.log 2>&1 ``` -## 🧰 常用命令 +你也可以在 Web 面板里修改发送窗口,Web 会更新同一个 cron 文件。 + +## 常用命令 ```bash docker compose ps @@ -134,26 +150,26 @@ docker compose logs -f web docker compose logs -f scheduler docker compose up -d --build docker compose down +./refresh_proxy.sh ``` -刷新代理订阅: +手动跑一次发送任务: ```bash -./refresh_proxy.sh -docker compose restart proxy +docker compose run --rm task ``` -## 📁 项目结构 +## 项目结构 - `DouYinSparkFlow/`:核心应用、Web UI、登录桌面和发送任务。 - `docker-compose.yml`:容器编排入口。 - `deploy/`:服务器和本地部署脚本。 -- `proxy/config.example.yaml`:代理配置模板。 +- `proxy/config.example.yaml`:直连代理配置模板。 - `.env.example`:部署环境变量模板。 -## 🗃️ 运行时文件 +## 运行时文件 -以下路径由应用在部署或运行时生成,通常不纳入版本控制。迁移或备份实例时,可根据实际需要处理这些文件: +以下文件由部署或运行生成,通常不纳入版本控制: - `.env` - `state/` @@ -161,17 +177,10 @@ docker compose restart proxy - `proxy/config.yaml` - `DouYinSparkFlow/logs/` - `DouYinSparkFlow/usersData.json` +- `DouYinSparkFlow/config.json` - `DouYinSparkFlow/webui_settings.json` - `DouYinSparkFlow/.im_sdk_cache/` -## 📈 GitHub Star 趋势 - -[![GitHub Star 趋势图](https://api.star-history.com/svg?repos=halfwaystudent/douyin-sparkflow&type=Date)](https://star-history.com/#halfwaystudent/douyin-sparkflow&Date) - -## 🤝 友情链接 - -- [Linux Do](https://linux.do/) - -## 📄 许可 +## 许可 核心应用采用 MIT 协议,详见 [DouYinSparkFlow/LICENSE](DouYinSparkFlow/LICENSE)。 diff --git a/deploy/install-local.ps1 b/deploy/install-local.ps1 index 62ae903..000d076 100644 --- a/deploy/install-local.ps1 +++ b/deploy/install-local.ps1 @@ -58,43 +58,6 @@ function Get-EnvValue { return $DefaultValue } -function Set-ProxyConfigLine { - param( - [string]$Path, - [string]$Key, - [string]$Value - ) - $line = "${Key}: $Value" - $content = Get-Content -Path $Path -ErrorAction SilentlyContinue - $escapedKey = [regex]::Escape($Key) - $found = $false - $next = foreach ($item in $content) { - if ($item -match "^${escapedKey}:") { - $found = $true - $line - } else { - $item - } - } - if (-not $found) { - $next = @($next) + $line - } - Set-Content -Path $Path -Value $next -Encoding utf8 -} - -function Refresh-ProxyConfig { - param([string]$Url) - if (-not $Url) { - return - } - $userAgent = Get-EnvValue -Path ".env" -Key "PROXY_USER_AGENT" -DefaultValue "clash-verge/1.7.7" - Invoke-WebRequest -Uri $Url -Headers @{ "User-Agent" = $userAgent } -OutFile "proxy/config.yaml" - Set-ProxyConfigLine -Path "proxy/config.yaml" -Key "mixed-port" -Value "7890" - Set-ProxyConfigLine -Path "proxy/config.yaml" -Key "allow-lan" -Value "true" - Set-ProxyConfigLine -Path "proxy/config.yaml" -Key "bind-address" -Value "'*'" - Set-ProxyConfigLine -Path "proxy/config.yaml" -Key "external-controller" -Value "'0.0.0.0:9090'" -} - Require-Command docker docker compose version | Out-Null @@ -110,8 +73,15 @@ New-Item -ItemType Directory -Force -Path "proxy", "state/cron", "state/login-pr if (-not (Test-Path "proxy/config.yaml")) { Copy-Item "proxy/config.example.yaml" "proxy/config.yaml" } +if (-not (Test-Path "state/cron/root") -or (Get-Item "state/cron/root").Length -eq 0) { + @( + "*/20 10-17 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1", + "0 18 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1", + "20 18 * * * cd /app && env SPARKFLOW_MANUAL_RUN=1 SPARKFLOW_MANUAL_UNSENT_ONLY=1 PYTHONUNBUFFERED=1 python main.py --doTask >> /app/logs/app.log 2>&1" + ) | Set-Content -Path "state/cron/root" -Encoding utf8 +} -Refresh-ProxyConfig -Url $ProxySubUrl +bash ./refresh_proxy.sh docker compose up -d --build diff --git a/deploy/install-local.sh b/deploy/install-local.sh index c38c63b..a74ceba 100755 --- a/deploy/install-local.sh +++ b/deploy/install-local.sh @@ -42,6 +42,13 @@ mkdir -p proxy state/cron state/login-profile DouYinSparkFlow/logs if [ ! -f "proxy/config.yaml" ]; then cp "proxy/config.example.yaml" "proxy/config.yaml" fi +if [ ! -s "state/cron/root" ]; then + cat > "state/cron/root" <<'CRON' +*/20 10-17 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1 +0 18 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1 +20 18 * * * cd /app && env SPARKFLOW_MANUAL_RUN=1 SPARKFLOW_MANUAL_UNSENT_ONLY=1 PYTHONUNBUFFERED=1 python main.py --doTask >> /app/logs/app.log 2>&1 +CRON +fi bash ./refresh_proxy.sh docker compose up -d --build diff --git a/deploy/install-server.sh b/deploy/install-server.sh index 66e9c4c..151fc9f 100755 --- a/deploy/install-server.sh +++ b/deploy/install-server.sh @@ -4,6 +4,8 @@ set -euo pipefail REPO_URL="${REPO_URL:-https://github.com/halfwaystudent/douyin-sparkflow.git}" BRANCH="${BRANCH:-main}" APP_ROOT="${APP_ROOT:-/opt/douyin-sparkflow}" +ACTION="${ACTION:-install}" +DEFAULT_SCHEDULE="${DEFAULT_SCHEDULE:-10:00-18:00/20m}" if [ "$(id -u)" -ne 0 ]; then SUDO="sudo" @@ -19,6 +21,10 @@ run_root() { fi } +log() { + printf '\n[install-server] %s\n' "$*" +} + install_base_tools() { if command -v curl >/dev/null 2>&1 && command -v git >/dev/null 2>&1 && command -v gpg >/dev/null 2>&1; then return @@ -28,6 +34,9 @@ install_base_tools() { run_root apt-get install -y ca-certificates curl git gnupg elif command -v yum >/dev/null 2>&1; then run_root yum install -y ca-certificates curl git + else + echo "Install curl, git, and ca-certificates first." >&2 + exit 1 fi } @@ -50,6 +59,7 @@ ensure_docker() { if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then return fi + log "Installing Docker and Compose plugin" if command -v apt-get >/dev/null 2>&1; then install_docker_debian elif command -v yum >/dev/null 2>&1; then @@ -64,11 +74,16 @@ ensure_docker() { prepare_repo() { run_root mkdir -p "$(dirname "$APP_ROOT")" if [ -d "$APP_ROOT/.git" ]; then - run_root git -C "$APP_ROOT" reset --hard + log "Updating existing repository at $APP_ROOT" run_root git -C "$APP_ROOT" fetch origin "$BRANCH" run_root git -C "$APP_ROOT" checkout -B "$BRANCH" "origin/$BRANCH" run_root git -C "$APP_ROOT" reset --hard "origin/$BRANCH" else + if [ -e "$APP_ROOT" ] && [ "$ACTION" = "install" ]; then + echo "$APP_ROOT exists but is not a git checkout. Move it aside or set ACTION=update after fixing it." >&2 + exit 1 + fi + log "Cloning $REPO_URL#$BRANCH into $APP_ROOT" run_root git clone --branch "$BRANCH" "$REPO_URL" "$APP_ROOT" fi } @@ -77,69 +92,121 @@ set_env_value() { local file="$1" local key="$2" local value="$3" - if grep -q "^${key}=" "$file"; then - local tmp_file - tmp_file="$(mktemp)" + local tmp_file + tmp_file="$(mktemp)" + if [ -f "$file" ]; then awk -v key="$key" -v value="$value" ' BEGIN { replaced = 0 } $0 ~ "^" key "=" { print key "=" value; replaced = 1; next } { print } END { if (!replaced) print key "=" value } ' "$file" > "$tmp_file" - run_root cp "$tmp_file" "$file" - rm -f "$tmp_file" else - printf '%s=%s\n' "$key" "$value" | run_root tee -a "$file" >/dev/null + printf '%s=%s\n' "$key" "$value" > "$tmp_file" fi + run_root cp "$tmp_file" "$file" + rm -f "$tmp_file" +} + +read_env_value() { + local file="$1" + local key="$2" + if [ ! -f "$file" ]; then + return 0 + fi + grep -E "^${key}=" "$file" | tail -n 1 | cut -d= -f2- || true +} + +write_default_cron() { + local cron_file="$APP_ROOT/state/cron/root" + if [ -s "$cron_file" ]; then + return + fi + if [ "$DEFAULT_SCHEDULE" != "10:00-18:00/20m" ]; then + echo "DEFAULT_SCHEDULE=$DEFAULT_SCHEDULE will be saved to .env. The initial cron file uses the built-in 10:00-18:00/20m schedule; adjust it from the Web UI after first login." >&2 + fi + cat > /tmp/douyin-sparkflow-cron <<'CRON' +*/20 10-17 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1 +0 18 * * * cd /app && python main.py --doTask >> /app/logs/app.log 2>&1 +20 18 * * * cd /app && env SPARKFLOW_MANUAL_RUN=1 SPARKFLOW_MANUAL_UNSENT_ONLY=1 PYTHONUNBUFFERED=1 python main.py --doTask >> /app/logs/app.log 2>&1 +CRON + run_root cp /tmp/douyin-sparkflow-cron "$cron_file" + rm -f /tmp/douyin-sparkflow-cron } prepare_runtime_files() { - if [ ! -f "$APP_ROOT/.env" ]; then - run_root cp "$APP_ROOT/.env.example" "$APP_ROOT/.env" + local env_file="$APP_ROOT/.env" + if [ ! -f "$env_file" ]; then + run_root cp "$APP_ROOT/.env.example" "$env_file" fi - set_env_value "$APP_ROOT/.env" "APP_ROOT" "$APP_ROOT" - for key in TZ WEB_PORT LOGIN_DESKTOP_WEB_PORT PROXY_HTTP_PORT PROXY_CONTROLLER_PORT PROXY_SUB_URL PLAYWRIGHT_BASE_IMAGE HTTP_PROXY_BUILD HTTPS_PROXY_BUILD ALL_PROXY_BUILD; do + + set_env_value "$env_file" "APP_ROOT" "$APP_ROOT" + set_env_value "$env_file" "DEFAULT_SCHEDULE" "$DEFAULT_SCHEDULE" + + for key in TZ WEB_PORT LOGIN_DESKTOP_WEB_PORT PROXY_HTTP_PORT PROXY_CONTROLLER_PORT PROXY_SUB_URL PROXY_USER_AGENT PLAYWRIGHT_BASE_IMAGE HTTP_PROXY_BUILD HTTPS_PROXY_BUILD ALL_PROXY_BUILD PIP_INDEX_URL PIP_TRUSTED_HOST; do if [ -n "${!key:-}" ]; then - set_env_value "$APP_ROOT/.env" "$key" "${!key}" + set_env_value "$env_file" "$key" "${!key}" fi done local current_sub - current_sub="$(grep '^PROXY_SUB_URL=' "$APP_ROOT/.env" | sed 's/^PROXY_SUB_URL=//' || true)" + current_sub="$(read_env_value "$env_file" PROXY_SUB_URL)" if [ -z "$current_sub" ] && [ -t 0 ]; then - printf 'Proxy subscription URL, optional and hidden: ' + printf 'Proxy subscription URL (optional, hidden; press Enter to skip): ' read -r -s input_sub || true printf '\n' if [ -n "${input_sub:-}" ]; then - set_env_value "$APP_ROOT/.env" "PROXY_SUB_URL" "$input_sub" + set_env_value "$env_file" "PROXY_SUB_URL" "$input_sub" fi fi - run_root mkdir -p "$APP_ROOT/proxy" "$APP_ROOT/state/cron" "$APP_ROOT/state/login-profile" "$APP_ROOT/DouYinSparkFlow/logs" + run_root mkdir -p \ + "$APP_ROOT/proxy" \ + "$APP_ROOT/state/cron" \ + "$APP_ROOT/state/login-profile" \ + "$APP_ROOT/DouYinSparkFlow/logs" + if [ ! -f "$APP_ROOT/proxy/config.yaml" ]; then run_root cp "$APP_ROOT/proxy/config.example.yaml" "$APP_ROOT/proxy/config.yaml" fi + write_default_cron } -main() { - install_base_tools - ensure_docker - prepare_repo - prepare_runtime_files +compose_up() { cd "$APP_ROOT" + log "Refreshing proxy configuration" run_root bash "$APP_ROOT/refresh_proxy.sh" - run_root env DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose up -d --build + log "Building and starting containers" + run_root env DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose up -d --build proxy web login-desktop scheduler +} +print_summary() { + local env_file="$APP_ROOT/.env" local web_port login_port host_ip - web_port="$(grep '^WEB_PORT=' "$APP_ROOT/.env" | sed 's/^WEB_PORT=//')" - login_port="$(grep '^LOGIN_DESKTOP_WEB_PORT=' "$APP_ROOT/.env" | sed 's/^LOGIN_DESKTOP_WEB_PORT=//')" + web_port="$(read_env_value "$env_file" WEB_PORT)" + login_port="$(read_env_value "$env_file" LOGIN_DESKTOP_WEB_PORT)" host_ip="$(hostname -I 2>/dev/null | awk '{print $1}')" host_ip="${host_ip:-127.0.0.1}" echo echo "Douyin SparkFlow is running." echo "Web UI: http://${host_ip}:${web_port:-8787}" echo "Login desktop: http://${host_ip}:${login_port:-8788}/vnc.html?autoconnect=1&resize=scale&view_only=0" - echo "Next: create the admin password, open the login desktop, scan the QR code, select target friends, and set the send window." + echo + echo "Runtime files preserved outside git: .env, state/, proxy/config.yaml, DouYinSparkFlow/logs/, usersData.json, webui_settings.json." + echo "Update later with: ACTION=update bash $APP_ROOT/deploy/install-server.sh" +} + +main() { + case "$ACTION" in + install|update) ;; + *) echo "ACTION must be install or update" >&2; exit 1 ;; + esac + install_base_tools + ensure_docker + prepare_repo + prepare_runtime_files + compose_up + print_summary } main "$@" diff --git a/docker-compose.yml b/docker-compose.yml index ad8aa2a..93feb4c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,6 +49,7 @@ services: volumes: - ./DouYinSparkFlow:/app - ./DouYinSparkFlow/logs:/app/logs + - /var/run/docker.sock:/var/run/docker.sock - ./state/cron:/host-spool-cron - .:/opt/douyin-sparkflow