| package main |
|
|
| import ( |
| "context" |
| "github.com/libaxuan/cursor2api-go/config" |
| "github.com/libaxuan/cursor2api-go/handlers" |
| "github.com/libaxuan/cursor2api-go/middleware" |
| "github.com/libaxuan/cursor2api-go/models" |
| "fmt" |
| "net/http" |
| "os" |
| "os/signal" |
| "strings" |
| "syscall" |
| "time" |
|
|
| "github.com/gin-gonic/gin" |
| "github.com/sirupsen/logrus" |
| ) |
|
|
| func main() { |
| |
| cfg, err := config.LoadConfig() |
| if err != nil { |
| logrus.Fatalf("Failed to load config: %v", err) |
| } |
|
|
| |
| if cfg.Debug { |
| logrus.SetLevel(logrus.DebugLevel) |
| gin.SetMode(gin.DebugMode) |
| } else { |
| logrus.SetLevel(logrus.InfoLevel) |
| gin.SetMode(gin.ReleaseMode) |
| } |
|
|
| |
| gin.DisableConsoleColor() |
|
|
| |
| router := gin.New() |
|
|
| |
| router.Use(gin.Recovery()) |
| router.Use(middleware.CORS()) |
| router.Use(middleware.ErrorHandler()) |
| |
| if cfg.Debug { |
| router.Use(gin.Logger()) |
| } |
|
|
| |
| handler := handlers.NewHandler(cfg) |
|
|
| |
| setupRoutes(router, handler) |
|
|
| |
| server := &http.Server{ |
| Addr: fmt.Sprintf(":%d", cfg.Port), |
| Handler: router, |
| } |
|
|
| |
| printStartupBanner(cfg) |
|
|
| |
| go func() { |
| if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { |
| logrus.Fatalf("Failed to start server: %v", err) |
| } |
| }() |
|
|
| |
| quit := make(chan os.Signal, 1) |
| signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) |
| <-quit |
| logrus.Info("Shutting down server...") |
|
|
| |
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) |
| defer cancel() |
| if err := server.Shutdown(ctx); err != nil { |
| logrus.Fatalf("Server forced to shutdown: %v", err) |
| } |
|
|
| logrus.Info("Server exited") |
| } |
|
|
| func setupRoutes(router *gin.Engine, handler *handlers.Handler) { |
| |
| router.GET("/health", func(c *gin.Context) { |
| c.JSON(http.StatusOK, gin.H{ |
| "status": "ok", |
| "time": time.Now().Unix(), |
| }) |
| }) |
|
|
| |
| router.GET("/", handler.ServeDocs) |
|
|
| |
| v1 := router.Group("/v1") |
| { |
| |
| v1.GET("/models", middleware.AuthRequired(), handler.ListModels) |
|
|
| |
| v1.POST("/chat/completions", middleware.AuthRequired(), handler.ChatCompletions) |
|
|
| |
| v1.POST("/responses", middleware.AuthRequired(), handler.Responses) |
| } |
|
|
| |
| router.Static("/static", "./static") |
| } |
|
|
| |
| func printStartupBanner(cfg *config.Config) { |
| banner := ` |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ |
| โ Cursor2API Server โ |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ |
| ` |
| fmt.Println(banner) |
|
|
| fmt.Printf("๐ ๆๅกๅฐๅ: http://localhost:%d\n", cfg.Port) |
| fmt.Printf("๐ API ๆๆกฃ: http://localhost:%d/\n", cfg.Port) |
| fmt.Printf("๐ ๅฅๅบทๆฃๆฅ: http://localhost:%d/health\n", cfg.Port) |
| fmt.Printf("๐ API ๅฏ้ฅ: %s\n", maskAPIKey(cfg.APIKey)) |
|
|
| modelList := cfg.GetModels() |
| fmt.Printf("\n๐ค ๆฏๆๆจกๅ (%d ไธช):\n", len(modelList)) |
|
|
| |
| providers := make(map[string][]string) |
| for _, modelID := range modelList { |
| if config, exists := models.GetModelConfig(modelID); exists { |
| providers[config.Provider] = append(providers[config.Provider], modelID) |
| } else { |
| providers["Other"] = append(providers["Other"], modelID) |
| } |
| } |
|
|
| |
| for _, provider := range []string{"Anthropic", "OpenAI", "Google", "Other"} { |
| if models, ok := providers[provider]; ok && len(models) > 0 { |
| fmt.Printf(" %s: %s\n", provider, strings.Join(models, ", ")) |
| } |
| } |
|
|
| if cfg.Debug { |
| fmt.Println("\n๐ ่ฐ่ฏๆจกๅผ: ๅทฒๅฏ็จ") |
| } |
|
|
| fmt.Println("\nโจ ๆๅกๅทฒๅฏๅจ๏ผๆ Ctrl+C ๅๆญข") |
| fmt.Println("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ") |
| } |
|
|
| |
| func maskAPIKey(key string) string { |
| if len(key) <= 4 { |
| return "****" |
| } |
| return key[:4] + "****" |
| } |
|
|