mirror of
https://github.com/Smile-QWQ/SubTracker.git
synced 2026-09-03 06:23:58 +08:00
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:
@@ -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
161
README.md
@@ -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
|
||||
|
||||

|
||||

|
||||
|
||||
### 更多截图
|
||||
### More views
|
||||
|
||||
| 订阅管理 | 费用统计 |
|
||||
| Subscriptions | Spending |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
| AI 识别 | Wallos 导入 |
|
||||
| AI recognition | Wallos import |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## 功能亮点
|
||||
## 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 3、Vite、TypeScript、Naive UI、Pinia、TanStack Query、ECharts
|
||||
- **后端**:Fastify、Prisma、SQLite、Zod、node-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.0(GPLv3)** 许可证发布
|
||||
|
||||
## 致谢
|
||||
|
||||
感谢以下项目和生态为 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
125
README.zh-CN.md
Normal 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.0(GPLv3)** 许可证发布。
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user