**功能概述**: 实现简易模式(Simple Mode),为个人用户和小团队提供简化的使用体验,隐藏复杂的分组、订阅、配额等概念。 **后端改动**: 1. 配置系统 - 新增 run_mode 配置项(standard/simple) - 支持环境变量 RUN_MODE - 默认值为 standard 2. 数据库初始化 - 自动创建3个默认分组:anthropic-default、openai-default、gemini-default - 默认分组配置:无并发限制、active状态、非独占 - 幂等性保证:重复启动不会重复创建 3. 账号管理 - 创建账号时自动绑定对应平台的默认分组 - 如果未指定分组,自动查找并绑定默认分组 **前端改动**: 1. 状态管理 - authStore 新增 isSimpleMode 计算属性 - 从后端API获取并同步运行模式 2. UI隐藏 - 侧边栏:隐藏分组管理、订阅管理、兑换码菜单 - 账号管理页面:隐藏分组列 - 创建/编辑账号对话框:隐藏分组选择器 3. 路由守卫 - 限制访问分组、订阅、兑换码相关页面 - 访问受限页面时自动重定向到仪表板 **配置示例**: ```yaml run_mode: simple run_mode: standard ``` **影响范围**: - 后端:配置、数据库迁移、账号服务 - 前端:认证状态、路由、UI组件 - 部署:配置文件示例 **兼容性**: - 简易模式和标准模式可无缝切换 - 不需要数据迁移 - 现有数据不受影响
154 lines
3.5 KiB
Go
154 lines
3.5 KiB
Go
package main
|
|
|
|
//go:generate go run github.com/google/wire/cmd/wire
|
|
|
|
import (
|
|
"context"
|
|
_ "embed"
|
|
"errors"
|
|
"flag"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/config"
|
|
"github.com/Wei-Shaw/sub2api/internal/handler"
|
|
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
|
|
"github.com/Wei-Shaw/sub2api/internal/setup"
|
|
"github.com/Wei-Shaw/sub2api/internal/web"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
//go:embed VERSION
|
|
var embeddedVersion string
|
|
|
|
// Build-time variables (can be set by ldflags)
|
|
var (
|
|
Version = ""
|
|
Commit = "unknown"
|
|
Date = "unknown"
|
|
BuildType = "source" // "source" for manual builds, "release" for CI builds (set by ldflags)
|
|
)
|
|
|
|
func init() {
|
|
// Read version from embedded VERSION file
|
|
Version = strings.TrimSpace(embeddedVersion)
|
|
if Version == "" {
|
|
Version = "0.0.0-dev"
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
// Parse command line flags
|
|
setupMode := flag.Bool("setup", false, "Run setup wizard in CLI mode")
|
|
showVersion := flag.Bool("version", false, "Show version information")
|
|
flag.Parse()
|
|
|
|
if *showVersion {
|
|
log.Printf("Sub2API %s (commit: %s, built: %s)\n", Version, Commit, Date)
|
|
return
|
|
}
|
|
|
|
// CLI setup mode
|
|
if *setupMode {
|
|
if err := setup.RunCLI(); err != nil {
|
|
log.Fatalf("Setup failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Check if setup is needed
|
|
if setup.NeedsSetup() {
|
|
// Check if auto-setup is enabled (for Docker deployment)
|
|
if setup.AutoSetupEnabled() {
|
|
log.Println("Auto setup mode enabled...")
|
|
if err := setup.AutoSetupFromEnv(); err != nil {
|
|
log.Fatalf("Auto setup failed: %v", err)
|
|
}
|
|
// Continue to main server after auto-setup
|
|
} else {
|
|
log.Println("First run detected, starting setup wizard...")
|
|
runSetupServer()
|
|
return
|
|
}
|
|
}
|
|
|
|
// Normal server mode
|
|
runMainServer()
|
|
}
|
|
|
|
func runSetupServer() {
|
|
r := gin.New()
|
|
r.Use(middleware.Recovery())
|
|
r.Use(middleware.CORS())
|
|
|
|
// Register setup routes
|
|
setup.RegisterRoutes(r)
|
|
|
|
// Serve embedded frontend if available
|
|
if web.HasEmbeddedFrontend() {
|
|
r.Use(web.ServeEmbeddedFrontend())
|
|
}
|
|
|
|
// Get server address from config.yaml or environment variables (SERVER_HOST, SERVER_PORT)
|
|
// This allows users to run setup on a different address if needed
|
|
addr := config.GetServerAddress()
|
|
log.Printf("Setup wizard available at http://%s", addr)
|
|
log.Println("Complete the setup wizard to configure Sub2API")
|
|
|
|
if err := r.Run(addr); err != nil {
|
|
log.Fatalf("Failed to start setup server: %v", err)
|
|
}
|
|
}
|
|
|
|
func runMainServer() {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
if cfg.RunMode == config.RunModeSimple {
|
|
log.Println("⚠️ WARNING: Running in SIMPLE mode - billing and quota checks are DISABLED")
|
|
}
|
|
|
|
buildInfo := handler.BuildInfo{
|
|
Version: Version,
|
|
BuildType: BuildType,
|
|
}
|
|
|
|
app, err := initializeApplication(buildInfo)
|
|
if err != nil {
|
|
log.Fatalf("Failed to initialize application: %v", err)
|
|
}
|
|
defer app.Cleanup()
|
|
|
|
// 启动服务器
|
|
go func() {
|
|
if err := app.Server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
}()
|
|
|
|
log.Printf("Server started on %s", app.Server.Addr)
|
|
|
|
// 等待中断信号
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
log.Println("Shutting down server...")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
if err := app.Server.Shutdown(ctx); err != nil {
|
|
log.Fatalf("Server forced to shutdown: %v", err)
|
|
}
|
|
|
|
log.Println("Server exited")
|
|
}
|