feat: refresh readme copy and bilingual installer flow

- rewrite the default README in natural GitHub-facing English, add a dedicated README.zh-CN.md companion, and align the deployment guide with the documented installer behavior

- add explicit installer language selection with bilingual prompts, summaries, generated install notes, and help output while keeping --lang available for scripted runs

- fix formatted installer messages so download and validation logs keep their interpolated values instead of dropping the target url or path
This commit is contained in:
SmileQWQ
2026-05-22 22:25:40 +08:00
parent c460c1e5fb
commit cb9aacf88b
4 changed files with 511 additions and 102 deletions

View File

@@ -18,6 +18,8 @@ API 容器首次启动时会自动执行 Prisma `db push`,自动初始化或
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash
```
脚本启动后会先询问使用 **中文** 还是 **English**;如果你希望跳过这一步,也可以用 `--lang zh``--lang en` 显式指定。
它会根据你选择的部署方式,自动把需要的文件下载到本地目录。
---
@@ -120,6 +122,7 @@ curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/i
--web-port <port> 完整部署前端端口,默认 8080
--web-origin <origin> WEB_ORIGIN
--log-level <level> LOG_LEVEL默认 warn
--lang <zh|en|auto> 安装脚本语言;交互模式下默认会先询问
--force 若目录已存在则覆盖
--yes 非交互模式,直接使用默认值
```

161
README.md
View File

@@ -5,7 +5,7 @@
<h1 align="center">SubTracker</h1>
<p align="center">
一个现代化的自托管订阅管理工具用来统一管理多币种订阅、续订提醒、预算分析、Logo 资源,以及 Wallos 数据迁移
Manage subscriptions, renewal reminders, budgets, logos, and Wallos migrations in one self-hosted dashboard.
</p>
<p align="center">
@@ -15,27 +15,29 @@
</p>
<p align="center">
<a href="#本地开发">本地开发</a> ·
<a href="#部署">部署</a> ·
<a href="./DEPLOYMENT.md">部署文档</a> ·
<a href="./README.zh-CN.md">简体中文</a> ·
<a href="#local-development">Local development</a> ·
<a href="#deployment">Deployment</a> ·
<a href="./DEPLOYMENT.md">Deployment guide</a> ·
<a href="https://github.com/Smile-QWQ/SubTracker/releases">Releases</a>
</p>
> 当前 `main` 分支提供 **Docker / Docker Compose** 部署;如果你需要 **Cloudflare Worker 无服务器部署**,请前往 [`lite`](https://github.com/Smile-QWQ/SubTracker/tree/lite) 分支,对应的部署说明、工作流与 Worker 适配实现都维护在该分支
> The current `main` branch ships with **Docker / Docker Compose** deployment. If you need **Cloudflare Worker serverless deployment**, use the [`lite`](https://github.com/Smile-QWQ/SubTracker/tree/lite) branch, where the Worker-specific workflows, deployment notes, and runtime adaptations are maintained.
## 快速开始
## Quick Start
### 想直接部署
### Deploy with the install script
```bash
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash
```
- 推荐使用 **完整部署full**
- 支持 **x86 / ARM** 双架构
- 详细步骤见 [`DEPLOYMENT.md`](./DEPLOYMENT.md)
- **Full mode** is recommended for most users.
- The installer now asks you to choose **中文** or **English** at the start. You can still force it with `--lang zh` or `--lang en`.
- Release artifacts support both **x86** and **ARM**.
- See [`DEPLOYMENT.md`](./DEPLOYMENT.md) for the full deployment flow.
### 想本地开发
### Run locally
```bash
npm install
@@ -45,62 +47,66 @@ npm run prisma:seed
npm run dev
```
默认地址:
Default addresses:
- Web`http://127.0.0.1:5173`
- API`http://127.0.0.1:3001`
- Web: `http://127.0.0.1:5173`
- API: `http://127.0.0.1:3001`
默认账户:
Default credentials:
- 用户名:`admin`
- 密码:`admin`
- Username: `admin`
- Password: `admin`
## 界面预览
## Screenshots
### 仪表盘
### Dashboard
![仪表盘](./screenshot/仪表盘.png)
![Dashboard](./screenshot/仪表盘.png)
### 更多截图
### More views
| 订阅管理 | 费用统计 |
| Subscriptions | Spending |
| --- | --- |
| ![订阅管理](./screenshot/订阅管理.png) | ![费用统计](./screenshot/费用统计.png) |
| ![Subscriptions](./screenshot/订阅管理.png) | ![Spending](./screenshot/费用统计.png) |
| AI 识别 | Wallos 导入 |
| AI recognition | Wallos import |
| --- | --- |
| ![AI识别](./screenshot/AI识别.png) | ![导入Wallos](./screenshot/导入Wallos.png) |
| ![AI recognition](./screenshot/AI识别.png) | ![Wallos import](./screenshot/导入Wallos.png) |
## 功能亮点
## Features
- **多币种订阅管理**:统一维护订阅名称、金额、计费周期、开始日期、下次续订时间、标签、自动续订状态,并支持暂停、停用、恢复、搜索、自定义排序和批量操作
- **灵活提醒规则**:支持到期前、当天、过期后的自定义提醒规则,按 `天数&时间;` 格式配置,适合同时覆盖提前提醒、当天提醒和过期补提醒
- **预算与统计视图**:提供月 / 年预算、未来 12 个月趋势、标签占比、状态分布、未来 30 天续订分布、订阅日历等视图,方便从总览和分类两个层面看支出
- **AI 辅助录入**:支持文本 / 图片识别自动填充订阅信息,并在统计页生成 AI 总结,减少手动录入和整理成本
- **备份与迁移**:支持原生 ZIP 备份导出、检查、导入与恢复,也兼容 Wallos 的 JSON、SQLite、ZIP 导入,迁移老数据会更顺手
- **Logo 与品牌资源**:支持上传、本地复用、网络搜索、导入匹配和品牌图展示,方便把订阅列表整理得更直观
- **登录与通知能力**:支持记住我、默认密码修改提醒、登录失败限流,以及 SMTP / Resend 邮件、PushPlus、Telegram Bot、Server 酱、Gotify、Webhook 等通知渠道
- **Subscription management**: create, edit, renew, pause, disable, and restore subscriptions; review renewal history; and manage larger collections with tags, multi-tag filtering, custom ordering, search, bulk status updates, and bulk deletion.
- **Reminder rules**: define reminders before renewal, on the renewal day, and after expiration with the `days&time;` format; override defaults per subscription; and preview the resulting trigger schedule before saving.
- **Statistics and budgets**: normalize multi-currency subscriptions into a base currency, track spending totals and trends, inspect tag and status breakdowns, review the next 30 days of renewals, compare auto-renew ratios, and configure monthly, yearly, or per-tag budgets.
- **AI assistance**: extract subscription details from text or images into the form, and generate an AI summary on the statistics page.
- **Calendar and overview**: view subscriptions in a calendar, track upcoming renewals from a dedicated list, and use the dashboard for a consolidated overview.
- **Notifications**: send reminders through Webhook, SMTP / Resend email, PushPlus, Telegram Bot, Server 酱, and Gotify.
- **Logos and assets**: upload logos, reuse saved local logos, search online, and preserve or match logos during Wallos ZIP imports when possible.
- **Backup and migration**: import Wallos JSON, SQLite, and ZIP backups, and export, inspect, import, or restore native SubTracker ZIP backups.
- **Multi-currency tools**: maintain exchange-rate data, convert values into the base currency, and use the built-in currency converter.
- **Login and session controls**: support remember-me sessions, default-password change reminders, and rate limiting after repeated login failures.
- **Interface**: support Simplified Chinese and English, light, dark, and system themes, with a sticky desktop sidebar and independently scrollable content.
## 技术栈
## Tech Stack
- **前端**Vue 3ViteTypeScriptNaive UIPiniaTanStack QueryECharts
- **后端**FastifyPrismaSQLiteZodnode-cron
- **Frontend**: Vue 3, Vite, TypeScript, Naive UI, Pinia, TanStack Query, ECharts
- **Backend**: Fastify, Prisma, SQLite, Zod, node-cron
## 本地开发
## Local Development
### 1. 安装依赖
### 1. Install dependencies
```bash
npm install
```
### 2. 复制开发环境变量
### 2. Copy the API environment template
```bash
cp apps/api/.env.example apps/api/.env
```
### 3. 初始化数据库
### 3. Initialize the database
```bash
npm run prisma:generate
@@ -108,15 +114,15 @@ npm run prisma:push
npm run prisma:seed
```
### 4. 启动开发环境
### 4. Start the dev environment
```bash
npm run dev
```
首次登录后建议立即修改默认密码;登录接口在连续失败过多时会触发限流保护
After the first login, changing the default admin password is strongly recommended. Login attempts are rate-limited after too many failures.
## 常用命令
## Useful Commands
```bash
npm run dev
@@ -125,73 +131,66 @@ npm run lint
npm test
```
## 部署
## Deployment
推荐直接使用安装脚本
Use the install script for the smoothest setup:
```bash
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash
```
脚本会按你选择的方式自动下载 Release 产物并生成部署目录:
The script downloads release artifacts, prepares the deployment directory, and lets you choose between:
- **完整部署full**:前端 + 后端一起部署,直接使用前端镜像
- **仅后端部署api**:只部署后端 API前端静态文件由你自己的 Nginx 托管
- **Full deployment (`full`)**: deploy web and API together with the published frontend image
- **API-only deployment (`api`)**: deploy only the API container and host the web assets yourself
推荐使用**完整部署**,步骤更少
**Full deployment** is the recommended default.
API 容器首次启动时会自动初始化 SQLite 数据库表结构
On first startup, the API container initializes the SQLite schema automatically.
### 升级
### Updating
日常升级直接拉取新镜像并重启:
For routine upgrades:
```bash
docker compose pull
docker compose up -d
```
仅后端部署升级时,还需要重新下载并覆盖 `subtracker-web-dist.zip` 解压后的前端静态文件目录
If you use API-only mode, you also need to download and replace the extracted contents of `subtracker-web-dist.zip`.
只有在这些场景下,才需要重新运行安装脚本:
You only need to rerun the install script when:
- 首次部署
- 想重建部署目录
- 想切换部署方式(`仅后端部署 / 完整部署`
- 部署模板或 `.env` 模板有明显变化
- deploying for the first time
- rebuilding the deployment directory
- switching between `api` and `full`
- adopting a newer deployment template or updated `.env` defaults
详细部署说明见 [`DEPLOYMENT.md`](./DEPLOYMENT.md)
See [`DEPLOYMENT.md`](./DEPLOYMENT.md) for the complete guide.
当前提供两种方式:
## Release Artifacts
1. **推荐**:完整部署,脚本准备部署目录后直接 `docker compose up -d`
2. **可选**:仅后端部署,外部 Nginx 托管前端静态文件Docker 仅部署 API
Each release currently provides:
## Release 产物
- `subtracker-web-dist.zip`: frontend static assets
- `ghcr.io/smile-qwq/subtracker-api`: API Docker image
- `ghcr.io/smile-qwq/subtracker-web`: frontend image used in full deployment
发布 Release 时会提供:
All published images support both x86 and ARM architectures.
- `subtracker-web-dist.zip`:前端静态文件
- `ghcr.io/smile-qwq/subtracker-api`API Docker 镜像
- `ghcr.io/smile-qwq/subtracker-web`:完整部署使用的前端 Docker 镜像
## License
以上 Docker 镜像均支持 x86 / ARM 双架构Docker 会根据宿主机架构自动拉取对应变体
This project is released under the **GNU General Public License v3.0 (GPLv3)**.
适合直接用于服务器部署
## Acknowledgements
## 许可证
Thanks to the following projects and ecosystems:
本项目采用 **GNU General Public License v3.0GPLv3** 许可证发布
## 致谢
感谢以下项目和生态为 SubTracker 提供支持:
- [Wallos](https://github.com/ellite/Wallos) —— 提供了导入兼容方向与迁移参考
- [Vue 3](https://vuejs.org/) 与 [Vite](https://vitejs.dev/) —— 提供前端开发基础
- [Naive UI](https://www.naiveui.com/) —— 提供界面组件支持
- [Fastify](https://fastify.dev/) 与 [Prisma](https://www.prisma.io/) —— 提供后端与数据访问能力
- [Pinia](https://pinia.vuejs.org/)、[TanStack Query](https://tanstack.com/query/latest) 与 [ECharts](https://echarts.apache.org/) —— 提供状态管理、数据请求与图表展示能力
- [Wallos](https://github.com/ellite/Wallos) — migration reference and compatibility direction
- [Vue 3](https://vuejs.org/) and [Vite](https://vitejs.dev/) — frontend foundation
- [Naive UI](https://www.naiveui.com/) — UI components
- [Fastify](https://fastify.dev/) and [Prisma](https://www.prisma.io/) — backend and data access
- [Pinia](https://pinia.vuejs.org/), [TanStack Query](https://tanstack.com/query/latest), and [ECharts](https://echarts.apache.org/) — state, data fetching, and charts
## Star History

125
README.zh-CN.md Normal file
View File

@@ -0,0 +1,125 @@
<p align="center">
<img src="./apps/web/src/assets/brand-logo.png" alt="SubTracker Logo" width="112" />
</p>
<h1 align="center">SubTracker</h1>
<p align="center">
一个现代化的自托管订阅管理工具用来统一管理订阅、提醒、预算、Logo 资源和 Wallos 数据迁移。
</p>
<p align="center">
<a href="./README.md">English</a> ·
<a href="#本地开发">本地开发</a> ·
<a href="#部署">部署</a> ·
<a href="./DEPLOYMENT.md">部署文档</a>
</p>
> 当前 `main` 分支提供 **Docker / Docker Compose** 部署;如果你需要 **Cloudflare Worker 无服务器部署**,请前往 [`lite`](https://github.com/Smile-QWQ/SubTracker/tree/lite) 分支,对应的部署说明、工作流与 Worker 适配实现都维护在该分支。
## 功能亮点
- **订阅管理**:支持新增、编辑、续订、暂停、停用、恢复和续订记录查看,并提供标签分类、多标签筛选、自定义排序、搜索、批量状态调整和批量删除。
- **提醒规则**:支持到期前、到期当天和过期提醒,使用 `天数&时间;` 格式配置;可按订阅覆盖默认规则,并提供提醒规则预览。
- **统计与预算**:支持多币种统一换算到基准货币,提供费用统计、趋势图表、标签统计、未来 30 天续订分布、状态分布和自动续订占比,并支持月预算、年预算和标签预算。
- **AI 能力**:支持通过文本或图片识别订阅信息并回填表单,统计页支持生成 AI 总结。
- **日历与总览**:提供订阅日历、即将续订列表和仪表盘总览。
- **通知通道**:支持 Webhook、SMTP / Resend 邮件、PushPlus、Telegram Bot、Server 酱和 Gotify。
- **Logo 与资源管理**:支持 Logo 上传、本地复用、网络搜索,并在导入 Wallos ZIP 时尽量匹配已有 Logo。
- **备份与迁移**:支持导入 Wallos 的 JSON、SQLite、ZIP 数据,并提供 SubTracker 原生 ZIP 备份的导出、检查、导入和恢复。
- **多币种工具**:提供汇率数据管理、基准货币换算和内置货币转换器。
- **登录与会话控制**:支持记住我、登录保留时长、默认密码修改提醒和登录失败限流。
- **界面体验**:支持简体中文和英文,以及浅色、深色和跟随系统主题,桌面端提供固定侧边栏和独立内容滚动。
## 快速开始
### 直接部署
```bash
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash
```
- 安装脚本启动后会先询问使用 **中文** 还是 **English**,也可以通过 `--lang zh``--lang en` 直接指定。
### 本地开发
```bash
npm install
npm run prisma:generate
npm run prisma:push
npm run prisma:seed
npm run dev
```
默认地址:
- Web`http://127.0.0.1:5173`
- API`http://127.0.0.1:3001`
默认账户:
- 用户名:`admin`
- 密码:`admin`
## 本地开发
### 1. 安装依赖
```bash
npm install
```
### 2. 复制环境变量
```bash
cp apps/api/.env.example apps/api/.env
```
### 3. 初始化数据库
```bash
npm run prisma:generate
npm run prisma:push
npm run prisma:seed
```
### 4. 启动开发环境
```bash
npm run dev
```
首次登录后建议尽快修改默认密码;登录失败过多时会触发限流保护。
## 常用命令
```bash
npm run dev
npm run build
npm run lint
npm test
```
## 部署
推荐直接使用安装脚本:
```bash
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash
```
可选模式:
- **完整部署full**:前端 + 后端一起部署
- **仅后端部署api**:只部署 API前端静态文件自行托管
推荐优先使用**完整部署**。更多细节见 [`DEPLOYMENT.md`](./DEPLOYMENT.md)。
## 技术栈
- **前端**Vue 3、Vite、TypeScript、Naive UI、Pinia、TanStack Query、ECharts
- **后端**Fastify、Prisma、SQLite、Zod、node-cron
## 许可证
本项目采用 **GNU General Public License v3.0GPLv3** 许可证发布。

View File

@@ -9,7 +9,9 @@ DEFAULT_WEB_IMAGE="ghcr.io/smile-qwq/subtracker-web:latest"
DEFAULT_API_PORT="3001"
DEFAULT_WEB_PORT="8080"
DEFAULT_LOG_LEVEL="warn"
DEFAULT_SCRIPT_LANG="en"
DEPLOYMENT_DOC_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/blob/main/DEPLOYMENT.md"
SCRIPT_LANG="${SUBTRACKER_LANG:-auto}"
MODE=""
INSTALL_DIR=""
@@ -24,11 +26,68 @@ NON_INTERACTIVE="false"
FORCE="false"
RESOLVED_REF=""
effective_lang() {
local lang_candidate="${SCRIPT_LANG:-$DEFAULT_SCRIPT_LANG}"
case "$lang_candidate" in
zh|en) ;;
*) lang_candidate="$DEFAULT_SCRIPT_LANG" ;;
esac
printf '%s' "$lang_candidate"
}
is_en() {
[ "$(effective_lang)" = "en" ]
}
can_prompt() {
[ "$NON_INTERACTIVE" != "true" ] && [ -r /dev/tty ] && [ -w /dev/tty ]
}
t() {
local lang
lang="$(effective_lang)"
case "$lang:$1" in
en:mode_full) printf '%s' 'Full deployment' ;;
zh:mode_full) printf '%s' '完整部署' ;;
en:mode_api) printf '%s' 'API-only deployment' ;;
zh:mode_api) printf '%s' '仅后端部署' ;;
en:missing_cmd) printf '%s' 'Missing required command: %s' ;;
zh:missing_cmd) printf '%s' '缺少依赖命令:%s' ;;
en:unknown_arg) printf '%s' 'Unknown argument: %s' ;;
zh:unknown_arg) printf '%s' '未知参数:%s' ;;
en:invalid_mode) printf '%s' '--mode only supports api or full, got: %s' ;;
zh:invalid_mode) printf '%s' '--mode 仅支持 api 或 full当前是%s' ;;
en:invalid_lang) printf '%s' '--lang only supports zh, en, or auto, got: %s' ;;
zh:invalid_lang) printf '%s' '--lang 仅支持 zh、en 或 auto当前是%s' ;;
en:invalid_lang_choice) printf '%s' 'Invalid choice. Please enter 1, 2, zh, or en.' ;;
zh:invalid_lang_choice) printf '%s' '输入无效,请输入 1、2、zh 或 en。' ;;
en:prompt_install_dir) printf '%s' 'Deployment directory (compose, .env, and data will be created here)' ;;
zh:prompt_install_dir) printf '%s' '部署目录(脚本会在这里生成 compose、.env、data' ;;
en:prompt_api_port) printf '%s' 'Public API port (API-only deployment)' ;;
zh:prompt_api_port) printf '%s' 'API 对外端口(仅后端部署)' ;;
en:prompt_web_port) printf '%s' 'Public web port (full deployment)' ;;
zh:prompt_web_port) printf '%s' '前端对外端口(完整部署)' ;;
en:prompt_web_origin) printf '%s' 'Final web origin for browser access / CORS, for example https://subtracker.example.com' ;;
zh:prompt_web_origin) printf '%s' '前端最终访问地址(用于浏览器跨域/CORS例如 https://subtracker.example.com' ;;
en:need_http_client) printf '%s' 'Either curl or wget is required to download deployment files' ;;
zh:need_http_client) printf '%s' '需要 curl 或 wget 才能下载部署文件' ;;
en:dir_exists) printf '%s' 'Directory already exists: %s. Use --force to overwrite it.' ;;
zh:dir_exists) printf '%s' '目录已存在:%s如需覆盖请加 --force' ;;
en:latest_release_unresolved) printf '%s' 'Failed to resolve the tag behind the latest Release' ;;
zh:latest_release_unresolved) printf '%s' '无法解析 latest Release 对应的 tag' ;;
en:downloading) printf '%s' 'Downloading: %s' ;;
zh:downloading) printf '%s' '下载文件:%s' ;;
en:env_template_missing) printf '%s' 'API environment template not found: %s' ;;
zh:env_template_missing) printf '%s' '未找到 API 环境变量模板:%s' ;;
*) printf '%s' "$1" ;;
esac
}
mode_label() {
if [ "${1:-$MODE}" = "full" ]; then
printf '完整部署'
t mode_full
else
printf '仅后端部署'
t mode_api
fi
}
@@ -46,12 +105,36 @@ fail() {
}
print_help() {
cat <<'EOF'
if is_en; then
cat <<'EOF'
SubTracker deployment installer
Usage:
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash -s -- --mode full --dir /opt/subtracker
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash -s -- --mode full --dir /opt/subtracker --lang en
Options:
--mode <api|full> Deployment mode: api = API-only, full = full deployment
--dir <path> Deployment directory, default: ./subtracker-<mode>
--release <tag|latest> Release tag to use, default: latest
--api-image <image> API image, default: ghcr.io/smile-qwq/subtracker-api:latest
--web-image <image> Web image for full deployment, default: ghcr.io/smile-qwq/subtracker-web:latest
--api-port <port> Public API port; in full mode the API still defaults to internal port 3001
--web-port <port> Public web port for full deployment, default: 8080
--web-origin <origin> Final web origin for CORS, for example https://subtracker.example.com
--log-level <level> API log level, default: warn
--lang <zh|en|auto> Installer language; auto prompts first when interactive
--force Overwrite the target directory if it already exists
--yes Non-interactive mode, use defaults for missing values
--help Show help
EOF
else
cat <<'EOF'
SubTracker deployment installer
Usage:
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash
curl -fsSL https://raw.githubusercontent.com/Smile-QWQ/SubTracker/main/scripts/install.sh | bash -s -- --mode full --dir /opt/subtracker --lang zh
Options:
--mode <api|full> 部署方式api=仅后端部署full=完整部署
@@ -63,14 +146,16 @@ Options:
--web-port <port> 完整部署前端对外端口,默认 8080
--web-origin <origin> 前端最终访问地址(用于 CORS例如 https://subtracker.example.com
--log-level <level> API 日志级别,默认 warn
--lang <zh|en|auto> 安装脚本语言;交互模式下 auto 会先询问
--force 若目录已存在则覆盖
--yes 非交互模式,缺省值直接使用默认值
--help 显示帮助
EOF
fi
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "缺少依赖命令:$1"
command -v "$1" >/dev/null 2>&1 || fail "$(printf "$(t missing_cmd)" "$1")"
}
parse_args() {
@@ -112,6 +197,10 @@ parse_args() {
LOG_LEVEL="${2:-}"
shift 2
;;
--lang)
SCRIPT_LANG="${2:-}"
shift 2
;;
--force)
FORCE="true"
shift
@@ -125,7 +214,7 @@ parse_args() {
exit 0
;;
*)
fail "未知参数:$1"
fail "$(printf "$(t unknown_arg)" "$1")"
;;
esac
done
@@ -141,7 +230,7 @@ prompt_value() {
return 0
fi
if [ "$NON_INTERACTIVE" = "true" ] || [ ! -r /dev/tty ]; then
if ! can_prompt; then
printf '%s' "$default_value"
return 0
fi
@@ -156,17 +245,69 @@ prompt_value() {
fi
}
select_language() {
case "$SCRIPT_LANG" in
zh|en) return 0 ;;
auto|'') ;;
*) fail "$(printf "$(t invalid_lang)" "$SCRIPT_LANG")" ;;
esac
if ! can_prompt; then
SCRIPT_LANG="$DEFAULT_SCRIPT_LANG"
return 0
fi
while true; do
cat > /dev/tty <<'EOF'
Select installer language / 请选择安装语言:
1) 简体中文
2) English
EOF
printf 'Language [1/2] (default: 2): ' > /dev/tty
local answer=""
IFS= read -r answer < /dev/tty || true
case "${answer:-2}" in
1|zh|ZH|zh-CN|zh-cn)
SCRIPT_LANG="zh"
return 0
;;
2|en|EN|en-US|en-us)
SCRIPT_LANG="en"
return 0
;;
*)
printf '%s / %s\n' "$(t invalid_lang_choice)" "$(SCRIPT_LANG=zh; t invalid_lang_choice)" > /dev/tty
;;
esac
done
}
select_mode() {
if [ -n "$MODE" ]; then
return 0
fi
if [ "$NON_INTERACTIVE" = "true" ] || [ ! -r /dev/tty ]; then
if ! can_prompt; then
MODE="full"
return 0
fi
cat > /dev/tty <<'EOF'
if is_en; then
cat > /dev/tty <<'EOF'
Choose a deployment mode:
api = API-only deployment
You host the frontend assets yourself with Nginx, aaPanel, or another static site setup.
full = Full deployment
Deploy frontend and backend together with the published web image.
EOF
printf 'Enter deployment mode [api/full] (default: full): ' > /dev/tty
else
cat > /dev/tty <<'EOF'
请选择部署方式:
api = 仅后端部署
@@ -176,32 +317,39 @@ select_mode() {
前端 + 后端一起部署,直接使用前端镜像
EOF
printf '请输入部署方式 [api/full](默认 full: ' > /dev/tty
printf '请输入部署方式 [api/full](默认 full: ' > /dev/tty
fi
local answer=""
IFS= read -r answer < /dev/tty || true
MODE="${answer:-full}"
}
normalize_inputs() {
case "$SCRIPT_LANG" in
auto|zh|en|'') ;;
*) fail "$(printf "$(t invalid_lang)" "$SCRIPT_LANG")" ;;
esac
select_language
select_mode
case "$MODE" in
api|full) ;;
*) fail "--mode 仅支持 api 或 full当前是$MODE" ;;
*) fail "$(printf "$(t invalid_mode)" "$MODE")" ;;
esac
if [ -z "$INSTALL_DIR" ]; then
INSTALL_DIR="$(prompt_value '部署目录(脚本会在这里生成 compose、.env、data' "./subtracker-${MODE}" "$INSTALL_DIR")"
INSTALL_DIR="$(prompt_value "$(t prompt_install_dir)" "./subtracker-${MODE}" "$INSTALL_DIR")"
fi
if [ "$MODE" = "api" ]; then
API_PORT="$(prompt_value 'API 对外端口(仅后端部署)' "$DEFAULT_API_PORT" "$API_PORT")"
API_PORT="$(prompt_value "$(t prompt_api_port)" "$DEFAULT_API_PORT" "$API_PORT")"
else
API_PORT="${API_PORT:-$DEFAULT_API_PORT}"
WEB_PORT="$(prompt_value '前端对外端口(完整部署)' "$DEFAULT_WEB_PORT" "$WEB_PORT")"
WEB_PORT="$(prompt_value "$(t prompt_web_port)" "$DEFAULT_WEB_PORT" "$WEB_PORT")"
fi
if [ -z "$WEB_ORIGIN" ]; then
WEB_ORIGIN="$(prompt_value '前端最终访问地址(用于浏览器跨域/CORS例如 https://subtracker.example.com' 'https://subtracker.example.com' "$WEB_ORIGIN")"
WEB_ORIGIN="$(prompt_value "$(t prompt_web_origin)" 'https://subtracker.example.com' "$WEB_ORIGIN")"
fi
}
@@ -213,7 +361,7 @@ http_get() {
elif command -v wget >/dev/null 2>&1; then
wget -qO "$output" "$url"
else
fail '需要 curl 或 wget 才能下载部署文件'
fail "$(t need_http_client)"
fi
}
@@ -222,7 +370,7 @@ prepare_dir() {
if [ "$FORCE" = "true" ]; then
rm -rf "$INSTALL_DIR"
else
fail "目录已存在:$INSTALL_DIR;如需覆盖请加 --force"
fail "$(printf "$(t dir_exists)" "$INSTALL_DIR")"
fi
fi
@@ -275,7 +423,7 @@ PY
fi
rm -f "$metadata_file"
[ -n "$RESOLVED_REF" ] || fail '无法解析 latest Release 对应的 tag'
[ -n "$RESOLVED_REF" ] || fail "$(t latest_release_unresolved)"
printf '%s' "$RESOLVED_REF"
}
@@ -291,7 +439,7 @@ download_repo_file() {
local target_path="$2"
local url
url="$(raw_file_url "$repo_path")"
info "下载文件:$url"
info "$(printf "$(t downloading)" "$url")"
mkdir -p "$(dirname "$target_path")"
http_get "$url" "$target_path"
}
@@ -301,7 +449,7 @@ write_env_file() {
local env_file="$INSTALL_DIR/.env"
if [ ! -f "$template_file" ]; then
fail "未找到 API 环境变量模板:$template_file"
fail "$(printf "$(t env_template_missing)" "$template_file")"
fi
cp "$template_file" "$env_file"
@@ -355,6 +503,101 @@ write_readme() {
compose_file='docker-compose.yml'
fi
if is_en; then
cat > "$INSTALL_DIR/INSTALL-README.md" <<EOF
# SubTracker ${mode_display} directory
This directory was generated automatically by the installer.
## Prepared files
- ${compose_file}
- .env
- data/
- data/logos/
EOF
if [ "$MODE" = "full" ]; then
cat >> "$INSTALL_DIR/INSTALL-README.md" <<EOF
- Web image for full deployment: ${WEB_IMAGE}
EOF
else
cat >> "$INSTALL_DIR/INSTALL-README.md" <<EOF
## Frontend static assets
- Download and host them yourself
- Asset: subtracker-web-dist.zip
- Download URL: $(release_asset_url 'subtracker-web-dist.zip')
EOF
fi
cat >> "$INSTALL_DIR/INSTALL-README.md" <<EOF
## WEB_ORIGIN
Set WEB_ORIGIN to the final browser-facing address, for example:
- https://subtracker.example.com
EOF
if [ "$MODE" = "api" ]; then
cat >> "$INSTALL_DIR/INSTALL-README.md" <<EOF
Typical API-only deployment flow:
- Browser -> https://your-domain.example
- Outer Nginx -> http://127.0.0.1:${API_PORT} (API)
- Frontend static assets -> hosted by your own Nginx
EOF
else
cat >> "$INSTALL_DIR/INSTALL-README.md" <<EOF
Typical full deployment flow:
- Browser -> https://your-domain.example
- Outer Nginx -> http://127.0.0.1:${WEB_PORT}
- Built-in web container -> API container
EOF
fi
cat >> "$INSTALL_DIR/INSTALL-README.md" <<EOF
## Start
cd ${INSTALL_DIR}
${pull_cmd}
${up_cmd}
## Default login
- Username: admin
- Password: admin
Change the default password after the first login. The app also reminds you to do so.
## Logs
cd ${INSTALL_DIR}
${logs_cmd}
## Upgrade
cd ${INSTALL_DIR}
${pull_cmd}
${up_cmd}
EOF
if [ "$MODE" = "api" ]; then
cat >> "$INSTALL_DIR/INSTALL-README.md" <<EOF
When upgrading an API-only deployment, you also need to download the latest subtracker-web-dist.zip and replace the hosted frontend files.
EOF
fi
return 0
fi
cat > "$INSTALL_DIR/INSTALL-README.md" <<EOF
# SubTracker ${mode_display}目录
@@ -462,6 +705,45 @@ show_summary() {
local compose_cmd='docker compose'
printf '\n'
if is_en; then
info "Deployment directory created: $INSTALL_DIR"
info "Deployment mode: $(mode_label)"
info "Release version: $(resolve_repo_ref)"
info "Next steps:"
printf '\n'
printf '1) Enter the deployment directory and review .env\n'
printf ' cd %s\n' "$INSTALL_DIR"
printf ' Edit .env as needed\n'
printf '\n'
printf '2) Pull images and start the stack\n'
printf ' %s pull\n' "$compose_cmd"
printf ' %s up -d\n' "$compose_cmd"
printf ' On first startup, the API container initializes the SQLite schema automatically\n'
printf '\n'
printf ' Default login: admin / admin\n'
printf ' Change the default password after the first login\n'
printf '\n'
printf '3) View logs\n'
printf ' %s logs -f api\n' "$compose_cmd"
if [ "$MODE" = "api" ]; then
printf '\n'
info "Frontend static assets: $(release_asset_url 'subtracker-web-dist.zip')"
else
printf '\n'
info "Web image: $WEB_IMAGE"
fi
printf '\n'
info "Detailed notes: $INSTALL_DIR/INSTALL-README.md"
info "Online deployment guide: $DEPLOYMENT_DOC_URL"
return 0
fi
info "部署目录已生成:$INSTALL_DIR"
info "部署方式:$(mode_label)"
info "Release 版本:$(resolve_repo_ref)"
@@ -501,9 +783,9 @@ show_summary() {
main() {
parse_args "$@"
normalize_inputs
need_cmd mkdir
need_cmd rm
normalize_inputs
prepare_dir
download_deployment_files
write_env_file