First commit

This commit is contained in:
shaw
2025-12-18 13:50:39 +08:00
parent 569f4882e5
commit 642842c29e
218 changed files with 86902 additions and 0 deletions

55
deploy/.env.example Normal file
View File

@@ -0,0 +1,55 @@
# =============================================================================
# Sub2API Docker Environment Configuration
# =============================================================================
# Copy this file to .env and modify as needed:
# cp .env.example .env
# nano .env
#
# Then start with: docker-compose up -d
# =============================================================================
# -----------------------------------------------------------------------------
# Server Configuration
# -----------------------------------------------------------------------------
# Bind address for host port mapping
BIND_HOST=0.0.0.0
# Server port (exposed on host)
SERVER_PORT=8080
# Server mode: release or debug
SERVER_MODE=release
# Timezone
TZ=Asia/Shanghai
# -----------------------------------------------------------------------------
# PostgreSQL Configuration (REQUIRED)
# -----------------------------------------------------------------------------
POSTGRES_USER=sub2api
POSTGRES_PASSWORD=change_this_secure_password
POSTGRES_DB=sub2api
# -----------------------------------------------------------------------------
# Redis Configuration
# -----------------------------------------------------------------------------
# Leave empty for no password (default for local development)
REDIS_PASSWORD=
REDIS_DB=0
# -----------------------------------------------------------------------------
# Admin Account
# -----------------------------------------------------------------------------
# Email for the admin account
ADMIN_EMAIL=admin@sub2api.local
# Password for admin account
# Leave empty to auto-generate (will be shown in logs on first run)
ADMIN_PASSWORD=
# -----------------------------------------------------------------------------
# JWT Configuration
# -----------------------------------------------------------------------------
# Leave empty to auto-generate (recommended)
JWT_SECRET=
JWT_EXPIRE_HOUR=24

258
deploy/README.md Normal file
View File

@@ -0,0 +1,258 @@
# Sub2API Deployment Files
This directory contains files for deploying Sub2API on Linux servers.
## Deployment Methods
| Method | Best For | Setup Wizard |
|--------|----------|--------------|
| **Docker Compose** | Quick setup, all-in-one | Not needed (auto-setup) |
| **Binary Install** | Production servers, systemd | Web-based wizard |
## Files
| File | Description |
|------|-------------|
| `docker-compose.yml` | Docker Compose configuration |
| `.env.example` | Docker environment variables template |
| `DOCKER.md` | Docker Hub documentation |
| `install.sh` | One-click binary installation script |
| `sub2api.service` | Systemd service unit file |
| `config.example.yaml` | Example configuration file |
---
## Docker Deployment (Recommended)
### Quick Start
```bash
# Clone repository
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api/deploy
# Configure environment
cp .env.example .env
nano .env # Set POSTGRES_PASSWORD (required)
# Start all services
docker-compose up -d
# View logs (check for auto-generated admin password)
docker-compose logs -f sub2api
# Access Web UI
# http://localhost:8080
```
### How Auto-Setup Works
When using Docker Compose with `AUTO_SETUP=true`:
1. On first run, the system automatically:
- Connects to PostgreSQL and Redis
- Creates all database tables
- Generates JWT secret (if not provided)
- Creates admin account (password auto-generated if not provided)
- Writes config.yaml
2. No manual Setup Wizard needed - just configure `.env` and start
3. If `ADMIN_PASSWORD` is not set, check logs for the generated password:
```bash
docker-compose logs sub2api | grep "admin password"
```
### Commands
```bash
# Start services
docker-compose up -d
# Stop services
docker-compose down
# View logs
docker-compose logs -f sub2api
# Restart Sub2API only
docker-compose restart sub2api
# Update to latest version
docker-compose pull
docker-compose up -d
# Remove all data (caution!)
docker-compose down -v
```
### Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `POSTGRES_PASSWORD` | **Yes** | - | PostgreSQL password |
| `SERVER_PORT` | No | `8080` | Server port |
| `ADMIN_EMAIL` | No | `admin@sub2api.local` | Admin email |
| `ADMIN_PASSWORD` | No | *(auto-generated)* | Admin password |
| `JWT_SECRET` | No | *(auto-generated)* | JWT secret |
| `TZ` | No | `Asia/Shanghai` | Timezone |
See `.env.example` for all available options.
---
## Binary Installation
For production servers using systemd.
### One-Line Installation
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
```
### Manual Installation
1. Download the latest release from [GitHub Releases](https://github.com/Wei-Shaw/sub2api/releases)
2. Extract and copy the binary to `/opt/sub2api/`
3. Copy `sub2api.service` to `/etc/systemd/system/`
4. Run:
```bash
sudo systemctl daemon-reload
sudo systemctl enable sub2api
sudo systemctl start sub2api
```
5. Open the Setup Wizard in your browser to complete configuration
### Commands
```bash
# Install
sudo ./install.sh
# Upgrade
sudo ./install.sh upgrade
# Uninstall
sudo ./install.sh uninstall
```
### Service Management
```bash
# Start the service
sudo systemctl start sub2api
# Stop the service
sudo systemctl stop sub2api
# Restart the service
sudo systemctl restart sub2api
# Check status
sudo systemctl status sub2api
# View logs
sudo journalctl -u sub2api -f
# Enable auto-start on boot
sudo systemctl enable sub2api
```
### Configuration
#### Server Address and Port
During installation, you will be prompted to configure the server listen address and port. These settings are stored in the systemd service file as environment variables.
To change after installation:
1. Edit the systemd service:
```bash
sudo systemctl edit sub2api
```
2. Add or modify:
```ini
[Service]
Environment=SERVER_HOST=0.0.0.0
Environment=SERVER_PORT=3000
```
3. Reload and restart:
```bash
sudo systemctl daemon-reload
sudo systemctl restart sub2api
```
#### Application Configuration
The main config file is at `/etc/sub2api/config.yaml` (created by Setup Wizard).
### Prerequisites
- Linux server (Ubuntu 20.04+, Debian 11+, CentOS 8+, etc.)
- PostgreSQL 14+
- Redis 6+
- systemd
### Directory Structure
```
/opt/sub2api/
├── sub2api # Main binary
├── sub2api.backup # Backup (after upgrade)
└── data/ # Runtime data
/etc/sub2api/
└── config.yaml # Configuration file
```
---
## Troubleshooting
### Docker
```bash
# Check container status
docker-compose ps
# View detailed logs
docker-compose logs --tail=100 sub2api
# Check database connection
docker-compose exec postgres pg_isready
# Check Redis connection
docker-compose exec redis redis-cli ping
# Restart all services
docker-compose restart
```
### Binary Install
```bash
# Check service status
sudo systemctl status sub2api
# View recent logs
sudo journalctl -u sub2api -n 50
# Check config file
sudo cat /etc/sub2api/config.yaml
# Check PostgreSQL
sudo systemctl status postgresql
# Check Redis
sudo systemctl status redis
```
### Common Issues
1. **Port already in use**: Change `SERVER_PORT` in `.env` or systemd config
2. **Database connection failed**: Check PostgreSQL is running and credentials are correct
3. **Redis connection failed**: Check Redis is running and password is correct
4. **Permission denied**: Ensure proper file ownership for binary install

View File

@@ -0,0 +1,89 @@
# Sub2API Configuration File
# Copy this file to /etc/sub2api/config.yaml and modify as needed
# Documentation: https://github.com/Wei-Shaw/sub2api
# =============================================================================
# Server Configuration
# =============================================================================
server:
# Bind address (0.0.0.0 for all interfaces)
host: "0.0.0.0"
# Port to listen on
port: 8080
# Mode: "debug" for development, "release" for production
mode: "release"
# =============================================================================
# Database Configuration (PostgreSQL)
# =============================================================================
database:
host: "localhost"
port: 5432
user: "postgres"
password: "your_secure_password_here"
dbname: "sub2api"
# SSL mode: disable, require, verify-ca, verify-full
sslmode: "disable"
# =============================================================================
# Redis Configuration
# =============================================================================
redis:
host: "localhost"
port: 6379
# Leave empty if no password is set
password: ""
# Database number (0-15)
db: 0
# =============================================================================
# JWT Configuration
# =============================================================================
jwt:
# IMPORTANT: Change this to a random string in production!
# Generate with: openssl rand -hex 32
secret: "change-this-to-a-secure-random-string"
# Token expiration time in hours
expire_hour: 24
# =============================================================================
# Default Settings
# =============================================================================
default:
# Initial admin account (created on first run)
admin_email: "admin@example.com"
admin_password: "admin123"
# Default settings for new users
user_concurrency: 5 # Max concurrent requests per user
user_balance: 0 # Initial balance for new users
# API key settings
api_key_prefix: "sk-" # Prefix for generated API keys
# Rate multiplier (affects billing calculation)
rate_multiplier: 1.0
# =============================================================================
# Rate Limiting
# =============================================================================
rate_limit:
# Cooldown time (in minutes) when upstream returns 529 (overloaded)
overload_cooldown_minutes: 10
# =============================================================================
# Pricing Data Source (Optional)
# =============================================================================
pricing:
# URL to fetch model pricing data (default: LiteLLM)
remote_url: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
# Hash verification URL (optional)
hash_url: ""
# Local data directory for caching
data_dir: "./data"
# Fallback pricing file
fallback_file: "./resources/model-pricing/model_prices_and_context_window.json"
# Update interval in hours
update_interval_hours: 24
# Hash check interval in minutes
hash_check_interval_minutes: 10

160
deploy/docker-compose.yml Normal file
View File

@@ -0,0 +1,160 @@
# =============================================================================
# Sub2API Docker Compose Configuration
# =============================================================================
# Quick Start:
# 1. Copy .env.example to .env and configure
# 2. docker-compose up -d
# 3. Check logs: docker-compose logs -f sub2api
# 4. Access: http://localhost:8080
#
# All configuration is done via environment variables.
# No Setup Wizard needed - the system auto-initializes on first run.
# =============================================================================
services:
# ===========================================================================
# Sub2API Application
# ===========================================================================
sub2api:
image: weishaw/sub2api:latest
container_name: sub2api
restart: unless-stopped
ports:
- "${BIND_HOST:-0.0.0.0}:${SERVER_PORT:-8080}:8080"
volumes:
# Data persistence (config.yaml will be auto-generated here)
- sub2api_data:/app/data
environment:
# =======================================================================
# Auto Setup (REQUIRED for Docker deployment)
# =======================================================================
- AUTO_SETUP=true
# =======================================================================
# Server Configuration
# =======================================================================
- SERVER_HOST=0.0.0.0
- SERVER_PORT=8080
- SERVER_MODE=${SERVER_MODE:-release}
# =======================================================================
# Database Configuration (PostgreSQL)
# =======================================================================
- DATABASE_HOST=postgres
- DATABASE_PORT=5432
- DATABASE_USER=${POSTGRES_USER:-sub2api}
- DATABASE_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
- DATABASE_DBNAME=${POSTGRES_DB:-sub2api}
- DATABASE_SSLMODE=disable
# =======================================================================
# Redis Configuration
# =======================================================================
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
- REDIS_DB=${REDIS_DB:-0}
# =======================================================================
# Admin Account (auto-created on first run)
# =======================================================================
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@sub2api.local}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
# =======================================================================
# JWT Configuration
# =======================================================================
# Leave empty to auto-generate (recommended)
- JWT_SECRET=${JWT_SECRET:-}
- JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24}
# =======================================================================
# Timezone Configuration
# This affects ALL time operations in the application:
# - Database timestamps
# - Usage statistics "today" boundary
# - Subscription expiry times
# - Log timestamps
# Common values: Asia/Shanghai, America/New_York, Europe/London, UTC
# =======================================================================
- TZ=${TZ:-Asia/Shanghai}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- sub2api-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# ===========================================================================
# PostgreSQL Database
# ===========================================================================
postgres:
image: postgres:15-alpine
container_name: sub2api-postgres
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${POSTGRES_USER:-sub2api}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
- POSTGRES_DB=${POSTGRES_DB:-sub2api}
- TZ=${TZ:-Asia/Shanghai}
networks:
- sub2api-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-sub2api} -d ${POSTGRES_DB:-sub2api}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# ===========================================================================
# Redis Cache
# ===========================================================================
redis:
image: redis:7-alpine
container_name: sub2api-redis
restart: unless-stopped
volumes:
- redis_data:/data
command: >
redis-server
--save 60 1
--appendonly yes
--appendfsync everysec
${REDIS_PASSWORD:+--requirepass ${REDIS_PASSWORD}}
environment:
- TZ=${TZ:-Asia/Shanghai}
networks:
- sub2api-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
# =============================================================================
# Volumes
# =============================================================================
volumes:
sub2api_data:
driver: local
postgres_data:
driver: local
redis_data:
driver: local
# =============================================================================
# Networks
# =============================================================================
networks:
sub2api-network:
driver: bridge

745
deploy/install.sh Normal file
View File

@@ -0,0 +1,745 @@
#!/bin/bash
#
# Sub2API Installation Script
# Sub2API 安装脚本
# Usage: curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | bash
#
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Configuration
GITHUB_REPO="Wei-Shaw/sub2api"
INSTALL_DIR="/opt/sub2api"
SERVICE_NAME="sub2api"
SERVICE_USER="sub2api"
CONFIG_DIR="/etc/sub2api"
# Server configuration (will be set by user)
SERVER_HOST="0.0.0.0"
SERVER_PORT="8080"
# Language (default: zh = Chinese)
LANG_CHOICE="zh"
# ============================================================
# Language strings / 语言字符串
# ============================================================
# Chinese strings
declare -A MSG_ZH=(
# General
["info"]="信息"
["success"]="成功"
["warning"]="警告"
["error"]="错误"
# Language selection
["select_lang"]="请选择语言 / Select language"
["lang_zh"]="中文"
["lang_en"]="English"
["enter_choice"]="请输入选择 (默认: 1)"
# Installation
["install_title"]="Sub2API 安装脚本"
["run_as_root"]="请使用 root 权限运行 (使用 sudo)"
["detected_platform"]="检测到平台"
["unsupported_arch"]="不支持的架构"
["unsupported_os"]="不支持的操作系统"
["missing_deps"]="缺少依赖"
["install_deps_first"]="请先安装以下依赖"
["fetching_version"]="正在获取最新版本..."
["latest_version"]="最新版本"
["failed_get_version"]="获取最新版本失败"
["downloading"]="正在下载"
["download_failed"]="下载失败"
["verifying_checksum"]="正在校验文件..."
["checksum_verified"]="校验通过"
["checksum_failed"]="校验失败"
["checksum_not_found"]="无法验证校验和checksums.txt 未找到)"
["extracting"]="正在解压..."
["binary_installed"]="二进制文件已安装到"
["user_exists"]="用户已存在"
["creating_user"]="正在创建系统用户"
["user_created"]="用户已创建"
["setting_up_dirs"]="正在设置目录..."
["dirs_configured"]="目录配置完成"
["installing_service"]="正在安装 systemd 服务..."
["service_installed"]="systemd 服务已安装"
["setting_up_sudoers"]="正在配置 sudoers..."
["sudoers_configured"]="sudoers 配置完成"
["sudoers_failed"]="sudoers 验证失败,已移除文件"
["ready_for_setup"]="准备就绪,可以启动设置向导"
# Completion
["install_complete"]="Sub2API 安装完成!"
["install_dir"]="安装目录"
["next_steps"]="后续步骤"
["step1_check_services"]="确保 PostgreSQL 和 Redis 正在运行:"
["step2_start_service"]="启动 Sub2API 服务:"
["step3_enable_autostart"]="设置开机自启:"
["step4_open_wizard"]="在浏览器中打开设置向导:"
["wizard_guide"]="设置向导将引导您完成:"
["wizard_db"]="数据库配置"
["wizard_redis"]="Redis 配置"
["wizard_admin"]="管理员账号创建"
["useful_commands"]="常用命令"
["cmd_status"]="查看状态"
["cmd_logs"]="查看日志"
["cmd_restart"]="重启服务"
["cmd_stop"]="停止服务"
# Upgrade
["upgrading"]="正在升级 Sub2API..."
["current_version"]="当前版本"
["stopping_service"]="正在停止服务..."
["backup_created"]="备份已创建"
["starting_service"]="正在启动服务..."
["upgrade_complete"]="升级完成!"
# Uninstall
["uninstall_confirm"]="这将从系统中移除 Sub2API。"
["are_you_sure"]="确定要继续吗?(y/N)"
["uninstall_cancelled"]="卸载已取消"
["removing_files"]="正在移除文件..."
["removing_install_dir"]="正在移除安装目录..."
["removing_user"]="正在移除用户..."
["config_not_removed"]="配置目录未被移除"
["remove_manually"]="如不再需要,请手动删除"
["uninstall_complete"]="Sub2API 已卸载"
# Help
["usage"]="用法"
["cmd_none"]="(无参数)"
["cmd_install"]="安装 Sub2API"
["cmd_upgrade"]="升级到最新版本"
["cmd_uninstall"]="卸载 Sub2API"
# Server configuration
["server_config_title"]="服务器配置"
["server_config_desc"]="配置 Sub2API 服务监听地址"
["server_host_prompt"]="服务器监听地址"
["server_host_hint"]="0.0.0.0 表示监听所有网卡127.0.0.1 仅本地访问"
["server_port_prompt"]="服务器端口"
["server_port_hint"]="建议使用 1024-65535 之间的端口"
["server_config_summary"]="服务器配置"
["invalid_port"]="无效端口号,请输入 1-65535 之间的数字"
)
# English strings
declare -A MSG_EN=(
# General
["info"]="INFO"
["success"]="SUCCESS"
["warning"]="WARNING"
["error"]="ERROR"
# Language selection
["select_lang"]="请选择语言 / Select language"
["lang_zh"]="中文"
["lang_en"]="English"
["enter_choice"]="Enter your choice (default: 1)"
# Installation
["install_title"]="Sub2API Installation Script"
["run_as_root"]="Please run as root (use sudo)"
["detected_platform"]="Detected platform"
["unsupported_arch"]="Unsupported architecture"
["unsupported_os"]="Unsupported OS"
["missing_deps"]="Missing dependencies"
["install_deps_first"]="Please install them first"
["fetching_version"]="Fetching latest version..."
["latest_version"]="Latest version"
["failed_get_version"]="Failed to get latest version"
["downloading"]="Downloading"
["download_failed"]="Download failed"
["verifying_checksum"]="Verifying checksum..."
["checksum_verified"]="Checksum verified"
["checksum_failed"]="Checksum verification failed"
["checksum_not_found"]="Could not verify checksum (checksums.txt not found)"
["extracting"]="Extracting..."
["binary_installed"]="Binary installed to"
["user_exists"]="User already exists"
["creating_user"]="Creating system user"
["user_created"]="User created"
["setting_up_dirs"]="Setting up directories..."
["dirs_configured"]="Directories configured"
["installing_service"]="Installing systemd service..."
["service_installed"]="Systemd service installed"
["setting_up_sudoers"]="Setting up sudoers..."
["sudoers_configured"]="Sudoers configured"
["sudoers_failed"]="Sudoers validation failed, removing file"
["ready_for_setup"]="Ready for Setup Wizard"
# Completion
["install_complete"]="Sub2API installation completed!"
["install_dir"]="Installation directory"
["next_steps"]="NEXT STEPS"
["step1_check_services"]="Make sure PostgreSQL and Redis are running:"
["step2_start_service"]="Start Sub2API service:"
["step3_enable_autostart"]="Enable auto-start on boot:"
["step4_open_wizard"]="Open the Setup Wizard in your browser:"
["wizard_guide"]="The Setup Wizard will guide you through:"
["wizard_db"]="Database configuration"
["wizard_redis"]="Redis configuration"
["wizard_admin"]="Admin account creation"
["useful_commands"]="USEFUL COMMANDS"
["cmd_status"]="Check status"
["cmd_logs"]="View logs"
["cmd_restart"]="Restart"
["cmd_stop"]="Stop"
# Upgrade
["upgrading"]="Upgrading Sub2API..."
["current_version"]="Current version"
["stopping_service"]="Stopping service..."
["backup_created"]="Backup created"
["starting_service"]="Starting service..."
["upgrade_complete"]="Upgrade completed!"
# Uninstall
["uninstall_confirm"]="This will remove Sub2API from your system."
["are_you_sure"]="Are you sure? (y/N)"
["uninstall_cancelled"]="Uninstall cancelled"
["removing_files"]="Removing files..."
["removing_install_dir"]="Removing installation directory..."
["removing_user"]="Removing user..."
["config_not_removed"]="Config directory was NOT removed."
["remove_manually"]="Remove it manually if you no longer need it."
["uninstall_complete"]="Sub2API has been uninstalled"
# Help
["usage"]="Usage"
["cmd_none"]="(none)"
["cmd_install"]="Install Sub2API"
["cmd_upgrade"]="Upgrade to the latest version"
["cmd_uninstall"]="Remove Sub2API"
# Server configuration
["server_config_title"]="Server Configuration"
["server_config_desc"]="Configure Sub2API server listen address"
["server_host_prompt"]="Server listen address"
["server_host_hint"]="0.0.0.0 listens on all interfaces, 127.0.0.1 for local only"
["server_port_prompt"]="Server port"
["server_port_hint"]="Recommended range: 1024-65535"
["server_config_summary"]="Server configuration"
["invalid_port"]="Invalid port number, please enter a number between 1-65535"
)
# Get message based on current language
msg() {
local key="$1"
if [ "$LANG_CHOICE" = "en" ]; then
echo "${MSG_EN[$key]}"
else
echo "${MSG_ZH[$key]}"
fi
}
# Print functions
print_info() {
echo -e "${BLUE}[$(msg 'info')]${NC} $1"
}
print_success() {
echo -e "${GREEN}[$(msg 'success')]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[$(msg 'warning')]${NC} $1"
}
print_error() {
echo -e "${RED}[$(msg 'error')]${NC} $1"
}
# Select language
select_language() {
echo ""
echo -e "${CYAN}=============================================="
echo " $(msg 'select_lang')"
echo "==============================================${NC}"
echo ""
echo " 1) $(msg 'lang_zh') (默认/default)"
echo " 2) $(msg 'lang_en')"
echo ""
# Read with timeout for piped input
read -t 10 -p "$(msg 'enter_choice'): " lang_input 2>/dev/null || lang_input=""
case "$lang_input" in
2|en|EN|english|English)
LANG_CHOICE="en"
;;
*)
LANG_CHOICE="zh"
;;
esac
echo ""
}
# Validate port number
validate_port() {
local port="$1"
if [[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then
return 0
fi
return 1
}
# Configure server settings
configure_server() {
echo ""
echo -e "${CYAN}=============================================="
echo " $(msg 'server_config_title')"
echo "==============================================${NC}"
echo ""
echo -e "${BLUE}$(msg 'server_config_desc')${NC}"
echo ""
# Server host
echo -e "${YELLOW}$(msg 'server_host_hint')${NC}"
read -p "$(msg 'server_host_prompt') [${SERVER_HOST}]: " input_host
if [ -n "$input_host" ]; then
SERVER_HOST="$input_host"
fi
echo ""
# Server port
echo -e "${YELLOW}$(msg 'server_port_hint')${NC}"
while true; do
read -p "$(msg 'server_port_prompt') [${SERVER_PORT}]: " input_port
if [ -z "$input_port" ]; then
# Use default
break
elif validate_port "$input_port"; then
SERVER_PORT="$input_port"
break
else
print_error "$(msg 'invalid_port')"
fi
done
echo ""
print_info "$(msg 'server_config_summary'): ${SERVER_HOST}:${SERVER_PORT}"
echo ""
}
# Check if running as root
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "$(msg 'run_as_root')"
exit 1
fi
}
# Detect OS and architecture
detect_platform() {
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$ARCH" in
x86_64)
ARCH="amd64"
;;
aarch64|arm64)
ARCH="arm64"
;;
*)
print_error "$(msg 'unsupported_arch'): $ARCH"
exit 1
;;
esac
case "$OS" in
linux)
OS="linux"
;;
darwin)
OS="darwin"
;;
*)
print_error "$(msg 'unsupported_os'): $OS"
exit 1
;;
esac
print_info "$(msg 'detected_platform'): ${OS}_${ARCH}"
}
# Check dependencies
check_dependencies() {
local missing=()
if ! command -v curl &> /dev/null; then
missing+=("curl")
fi
if ! command -v tar &> /dev/null; then
missing+=("tar")
fi
if [ ${#missing[@]} -gt 0 ]; then
print_error "$(msg 'missing_deps'): ${missing[*]}"
print_info "$(msg 'install_deps_first')"
exit 1
fi
}
# Get latest release version
get_latest_version() {
print_info "$(msg 'fetching_version')"
LATEST_VERSION=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
if [ -z "$LATEST_VERSION" ]; then
print_error "$(msg 'failed_get_version')"
exit 1
fi
print_info "$(msg 'latest_version'): $LATEST_VERSION"
}
# Download and extract
download_and_extract() {
local version_num=${LATEST_VERSION#v}
local archive_name="sub2api_${version_num}_${OS}_${ARCH}.tar.gz"
local download_url="https://github.com/${GITHUB_REPO}/releases/download/${LATEST_VERSION}/${archive_name}"
local checksum_url="https://github.com/${GITHUB_REPO}/releases/download/${LATEST_VERSION}/checksums.txt"
print_info "$(msg 'downloading') ${archive_name}..."
# Create temp directory
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
# Download archive
if ! curl -sL "$download_url" -o "$TEMP_DIR/$archive_name"; then
print_error "$(msg 'download_failed')"
exit 1
fi
# Download and verify checksum
print_info "$(msg 'verifying_checksum')"
if curl -sL "$checksum_url" -o "$TEMP_DIR/checksums.txt" 2>/dev/null; then
local expected_checksum=$(grep "$archive_name" "$TEMP_DIR/checksums.txt" | awk '{print $1}')
local actual_checksum=$(sha256sum "$TEMP_DIR/$archive_name" | awk '{print $1}')
if [ "$expected_checksum" != "$actual_checksum" ]; then
print_error "$(msg 'checksum_failed')"
print_error "Expected: $expected_checksum"
print_error "Actual: $actual_checksum"
exit 1
fi
print_success "$(msg 'checksum_verified')"
else
print_warning "$(msg 'checksum_not_found')"
fi
# Extract
print_info "$(msg 'extracting')"
tar -xzf "$TEMP_DIR/$archive_name" -C "$TEMP_DIR"
# Create install directory
mkdir -p "$INSTALL_DIR"
# Copy binary
cp "$TEMP_DIR/sub2api" "$INSTALL_DIR/sub2api"
chmod +x "$INSTALL_DIR/sub2api"
# Copy deploy files if they exist in the archive
if [ -d "$TEMP_DIR/deploy" ]; then
cp -r "$TEMP_DIR/deploy/"* "$INSTALL_DIR/" 2>/dev/null || true
fi
print_success "$(msg 'binary_installed') $INSTALL_DIR/sub2api"
}
# Create system user
create_user() {
if id "$SERVICE_USER" &>/dev/null; then
print_info "$(msg 'user_exists'): $SERVICE_USER"
else
print_info "$(msg 'creating_user') $SERVICE_USER..."
useradd -r -s /bin/false -d "$INSTALL_DIR" "$SERVICE_USER"
print_success "$(msg 'user_created')"
fi
}
# Setup directories and permissions
setup_directories() {
print_info "$(msg 'setting_up_dirs')"
# Create directories
mkdir -p "$INSTALL_DIR"
mkdir -p "$INSTALL_DIR/data"
mkdir -p "$CONFIG_DIR"
# Set ownership
chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR"
chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR"
print_success "$(msg 'dirs_configured')"
}
# Setup sudoers for service restart
setup_sudoers() {
print_info "$(msg 'setting_up_sudoers')"
# Check if sudoers file exists in install dir
if [ -f "$INSTALL_DIR/sub2api-sudoers" ]; then
cp "$INSTALL_DIR/sub2api-sudoers" /etc/sudoers.d/sub2api
else
# Create sudoers file
cat > /etc/sudoers.d/sub2api << 'EOF'
# Sudoers configuration for Sub2API
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl restart sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl stop sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl start sub2api
EOF
fi
# Set correct permissions (required for sudoers files)
chmod 440 /etc/sudoers.d/sub2api
# Validate sudoers file
if visudo -c -f /etc/sudoers.d/sub2api &>/dev/null; then
print_success "$(msg 'sudoers_configured')"
else
print_warning "$(msg 'sudoers_failed')"
rm -f /etc/sudoers.d/sub2api
fi
}
# Install systemd service
install_service() {
print_info "$(msg 'installing_service')"
# Create service file with configured host and port
cat > /etc/systemd/system/sub2api.service << EOF
[Unit]
Description=Sub2API - AI API Gateway Platform
Documentation=https://github.com/Wei-Shaw/sub2api
After=network.target postgresql.service redis.service
Wants=postgresql.service redis.service
[Service]
Type=simple
User=sub2api
Group=sub2api
WorkingDirectory=/opt/sub2api
ExecStart=/opt/sub2api/sub2api
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sub2api
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/sub2api
# Environment - Server configuration
Environment=GIN_MODE=release
Environment=SERVER_HOST=${SERVER_HOST}
Environment=SERVER_PORT=${SERVER_PORT}
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd
systemctl daemon-reload
print_success "$(msg 'service_installed')"
}
# Prepare for setup wizard (no config file needed - setup wizard will create it)
prepare_for_setup() {
print_success "$(msg 'ready_for_setup')"
}
# Print completion message
print_completion() {
local ip_addr
ip_addr=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "YOUR_SERVER_IP")
# Determine display address
local display_host="$ip_addr"
if [ "$SERVER_HOST" = "127.0.0.1" ]; then
display_host="127.0.0.1"
fi
echo ""
echo "=============================================="
print_success "$(msg 'install_complete')"
echo "=============================================="
echo ""
echo "$(msg 'install_dir'): $INSTALL_DIR"
echo "$(msg 'server_config_summary'): ${SERVER_HOST}:${SERVER_PORT}"
echo ""
echo "=============================================="
echo " $(msg 'next_steps')"
echo "=============================================="
echo ""
echo " 1. $(msg 'step1_check_services')"
echo " sudo systemctl status postgresql"
echo " sudo systemctl status redis"
echo ""
echo " 2. $(msg 'step2_start_service')"
echo " sudo systemctl start sub2api"
echo ""
echo " 3. $(msg 'step3_enable_autostart')"
echo " sudo systemctl enable sub2api"
echo ""
echo " 4. $(msg 'step4_open_wizard')"
echo ""
print_info " http://${display_host}:${SERVER_PORT}"
echo ""
echo " $(msg 'wizard_guide')"
echo " - $(msg 'wizard_db')"
echo " - $(msg 'wizard_redis')"
echo " - $(msg 'wizard_admin')"
echo ""
echo "=============================================="
echo " $(msg 'useful_commands')"
echo "=============================================="
echo ""
echo " $(msg 'cmd_status'): sudo systemctl status sub2api"
echo " $(msg 'cmd_logs'): sudo journalctl -u sub2api -f"
echo " $(msg 'cmd_restart'): sudo systemctl restart sub2api"
echo " $(msg 'cmd_stop'): sudo systemctl stop sub2api"
echo ""
echo "=============================================="
}
# Upgrade function
upgrade() {
print_info "$(msg 'upgrading')"
# Get current version
if [ -f "$INSTALL_DIR/sub2api" ]; then
CURRENT_VERSION=$("$INSTALL_DIR/sub2api" --version 2>/dev/null | grep -oP 'v?\d+\.\d+\.\d+' || echo "unknown")
print_info "$(msg 'current_version'): $CURRENT_VERSION"
fi
# Stop service
if systemctl is-active --quiet sub2api; then
print_info "$(msg 'stopping_service')"
systemctl stop sub2api
fi
# Backup current binary
if [ -f "$INSTALL_DIR/sub2api" ]; then
cp "$INSTALL_DIR/sub2api" "$INSTALL_DIR/sub2api.backup"
print_info "$(msg 'backup_created'): $INSTALL_DIR/sub2api.backup"
fi
# Download and install new version
get_latest_version
download_and_extract
# Set permissions
chown "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR/sub2api"
# Start service
print_info "$(msg 'starting_service')"
systemctl start sub2api
print_success "$(msg 'upgrade_complete')"
}
# Uninstall function
uninstall() {
print_warning "$(msg 'uninstall_confirm')"
read -p "$(msg 'are_you_sure') " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "$(msg 'uninstall_cancelled')"
exit 0
fi
print_info "$(msg 'stopping_service')"
systemctl stop sub2api 2>/dev/null || true
systemctl disable sub2api 2>/dev/null || true
print_info "$(msg 'removing_files')"
rm -f /etc/systemd/system/sub2api.service
rm -f /etc/sudoers.d/sub2api
systemctl daemon-reload
print_info "$(msg 'removing_install_dir')"
rm -rf "$INSTALL_DIR"
print_info "$(msg 'removing_user')"
userdel "$SERVICE_USER" 2>/dev/null || true
print_warning "$(msg 'config_not_removed'): $CONFIG_DIR"
print_warning "$(msg 'remove_manually')"
print_success "$(msg 'uninstall_complete')"
}
# Main
main() {
# Select language first
select_language
echo ""
echo "=============================================="
echo " $(msg 'install_title')"
echo "=============================================="
echo ""
# Parse arguments
case "${1:-}" in
upgrade|update)
check_root
detect_platform
upgrade
exit 0
;;
uninstall|remove)
check_root
uninstall
exit 0
;;
--help|-h)
echo "$(msg 'usage'): $0 [command]"
echo ""
echo "Commands:"
echo " $(msg 'cmd_none') $(msg 'cmd_install')"
echo " upgrade $(msg 'cmd_upgrade')"
echo " uninstall $(msg 'cmd_uninstall')"
echo ""
exit 0
;;
esac
# Fresh install
check_root
detect_platform
check_dependencies
configure_server
get_latest_version
download_and_extract
create_user
setup_directories
install_service
setup_sudoers
prepare_for_setup
print_completion
}
main "$@"

13
deploy/sub2api-sudoers Normal file
View File

@@ -0,0 +1,13 @@
# Sudoers configuration for Sub2API
# This file allows the sub2api service user to restart the service without password
#
# Installation:
# sudo cp sub2api-sudoers /etc/sudoers.d/sub2api
# sudo chmod 440 /etc/sudoers.d/sub2api
#
# SECURITY NOTE: This grants limited sudo access only for service management
# Allow sub2api user to restart the service without password
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl restart sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl stop sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl start sub2api

33
deploy/sub2api.service Normal file
View File

@@ -0,0 +1,33 @@
[Unit]
Description=Sub2API - AI API Gateway Platform
Documentation=https://github.com/Wei-Shaw/sub2api
After=network.target postgresql.service redis.service
Wants=postgresql.service redis.service
[Service]
Type=simple
User=sub2api
Group=sub2api
WorkingDirectory=/opt/sub2api
ExecStart=/opt/sub2api/sub2api
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sub2api
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/sub2api
# Environment - Server configuration
# Modify these values to change listen address and port
Environment=GIN_MODE=release
Environment=SERVER_HOST=0.0.0.0
Environment=SERVER_PORT=8080
[Install]
WantedBy=multi-user.target