From 0a9ff6b58260b7c308a31632c7b267c543069028 Mon Sep 17 00:00:00 2001 From: Jason Lyu Date: Tue, 8 Apr 2025 13:53:20 -0400 Subject: [PATCH] refactor: make service exportable (#183) --- main.go | 261 +------------------------------ config.go => service/config.go | 8 +- service/service.go | 275 +++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 258 deletions(-) rename config.go => service/config.go (93%) create mode 100644 service/service.go diff --git a/main.go b/main.go index 6018f09..baba880 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,8 @@ /* * @Author: Vincent Yang * @Date: 2023-07-01 21:45:34 - * @LastEditors: Vincent Yang - * @LastEditTime: 2024-11-01 13:04:50 + * @LastEditors: Jason Lyu + * @LastEditTime: 2025-04-08 13:45:00 * @FilePath: /DeepLX/main.go * @Telegram: https://t.me/missuo * @GitHub: https://github.com/missuo @@ -14,268 +14,21 @@ package main import ( "fmt" - "log" - "net/http" - "net/url" - "os" - "strings" - translate "github.com/OwO-Network/DeepLX/translate" - "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" + + "github.com/OwO-Network/DeepLX/service" ) -func authMiddleware(cfg *Config) gin.HandlerFunc { - return func(c *gin.Context) { - if cfg.Token != "" { - providedTokenInQuery := c.Query("token") - providedTokenInHeader := c.GetHeader("Authorization") - - // Compatability with the Bearer token format - if providedTokenInHeader != "" { - parts := strings.Split(providedTokenInHeader, " ") - if len(parts) == 2 { - if parts[0] == "Bearer" || parts[0] == "DeepL-Auth-Key" { - providedTokenInHeader = parts[1] - } else { - providedTokenInHeader = "" - } - } else { - providedTokenInHeader = "" - } - } - - if providedTokenInHeader != cfg.Token && providedTokenInQuery != cfg.Token { - c.JSON(http.StatusUnauthorized, gin.H{ - "code": http.StatusUnauthorized, - "message": "Invalid access token", - }) - c.Abort() - return - } - } - - c.Next() - } -} - -type PayloadFree struct { - TransText string `json:"text"` - SourceLang string `json:"source_lang"` - TargetLang string `json:"target_lang"` - TagHandling string `json:"tag_handling"` -} - -type PayloadAPI struct { - Text []string `json:"text"` - TargetLang string `json:"target_lang"` - SourceLang string `json:"source_lang"` - TagHandling string `json:"tag_handling"` -} - func main() { - cfg := initConfig() + cfg := service.InitConfig() fmt.Printf("DeepL X has been successfully launched! Listening on %v:%v\n", cfg.IP, cfg.Port) fmt.Println("Developed by sjlleo and missuo .") - // Set Proxy - proxyURL := os.Getenv("PROXY") - if proxyURL == "" { - proxyURL = cfg.Proxy - } - if proxyURL != "" { - proxy, err := url.Parse(proxyURL) - if err != nil { - log.Fatalf("Failed to parse proxy URL: %v", err) - } - http.DefaultTransport = &http.Transport{ - Proxy: http.ProxyURL(proxy), - } - } - - if cfg.Token != "" { - fmt.Println("Access token is set.") - } - // Setting the application to release mode gin.SetMode(gin.ReleaseMode) - r := gin.Default() - r.Use(cors.Default()) - // Defining the root endpoint which returns the project details - r.GET("/", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "message": "DeepL Free API, Developed by sjlleo and missuo. Go to /translate with POST. http://github.com/OwO-Network/DeepLX", - }) - }) - - // Free API endpoint, No Pro Account required - r.POST("/translate", authMiddleware(cfg), func(c *gin.Context) { - req := PayloadFree{} - c.BindJSON(&req) - - sourceLang := req.SourceLang - targetLang := req.TargetLang - translateText := req.TransText - tagHandling := req.TagHandling - - proxyURL := cfg.Proxy - - if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" { - c.JSON(http.StatusBadRequest, gin.H{ - "code": http.StatusBadRequest, - "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.", - }) - return - } - - result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, "") - if err != nil { - log.Fatalf("Translation failed: %s", err) - } - - if result.Code == http.StatusOK { - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "id": result.ID, - "data": result.Data, - "alternatives": result.Alternatives, - "source_lang": result.SourceLang, - "target_lang": result.TargetLang, - "method": result.Method, - }) - } else { - c.JSON(result.Code, gin.H{ - "code": result.Code, - "message": result.Message, - }) - - } - }) - - // Pro API endpoint, Pro Account required - r.POST("/v1/translate", authMiddleware(cfg), func(c *gin.Context) { - req := PayloadFree{} - c.BindJSON(&req) - - sourceLang := req.SourceLang - targetLang := req.TargetLang - translateText := req.TransText - tagHandling := req.TagHandling - proxyURL := cfg.Proxy - - dlSession := cfg.DlSession - - if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" { - c.JSON(http.StatusBadRequest, gin.H{ - "code": http.StatusBadRequest, - "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.", - }) - return - } - - cookie := c.GetHeader("Cookie") - if cookie != "" { - dlSession = strings.Replace(cookie, "dl_session=", "", -1) - } - - if dlSession == "" { - c.JSON(http.StatusUnauthorized, gin.H{ - "code": http.StatusUnauthorized, - "message": "No dl_session Found", - }) - return - } else if strings.Contains(dlSession, ".") { - c.JSON(http.StatusUnauthorized, gin.H{ - "code": http.StatusUnauthorized, - "message": "Your account is not a Pro account. Please upgrade your account or switch to a different account.", - }) - return - } - - result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, dlSession) - if err != nil { - log.Fatalf("Translation failed: %s", err) - } - - if result.Code == http.StatusOK { - c.JSON(http.StatusOK, gin.H{ - "code": http.StatusOK, - "id": result.ID, - "data": result.Data, - "alternatives": result.Alternatives, - "source_lang": result.SourceLang, - "target_lang": result.TargetLang, - "method": result.Method, - }) - } else { - c.JSON(result.Code, gin.H{ - "code": result.Code, - "message": result.Message, - }) - - } - }) - - // Free API endpoint, Consistent with the official API format - r.POST("/v2/translate", authMiddleware(cfg), func(c *gin.Context) { - proxyURL := cfg.Proxy - - var translateText string - var targetLang string - - translateText = c.PostForm("text") - targetLang = c.PostForm("target_lang") - - if translateText == "" || targetLang == "" { - var jsonData struct { - Text []string `json:"text"` - TargetLang string `json:"target_lang"` - } - - if err := c.BindJSON(&jsonData); err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "code": http.StatusBadRequest, - "message": "Invalid request payload", - }) - return - } - - translateText = strings.Join(jsonData.Text, "\n") - targetLang = jsonData.TargetLang - } - - result, err := translate.TranslateByDeepLX("", targetLang, translateText, "", proxyURL, "") - if err != nil { - log.Fatalf("Translation failed: %s", err) - } - - if result.Code == http.StatusOK { - c.JSON(http.StatusOK, gin.H{ - "translations": []map[string]interface{}{ - { - "detected_source_language": result.SourceLang, - "text": result.Data, - }, - }, - }) - } else { - c.JSON(result.Code, gin.H{ - "code": result.Code, - "message": result.Message, - }) - } - }) - - // Catch-all route to handle undefined paths - r.NoRoute(func(c *gin.Context) { - c.JSON(http.StatusNotFound, gin.H{ - "code": http.StatusNotFound, - "message": "Path not found", - }) - }) - - r.Run(fmt.Sprintf("%v:%v", cfg.IP, cfg.Port)) + app := service.Router(cfg) + app.Run(fmt.Sprintf("%v:%v", cfg.IP, cfg.Port)) } diff --git a/config.go b/service/config.go similarity index 93% rename from config.go rename to service/config.go index beb0fac..b1cb4c3 100644 --- a/config.go +++ b/service/config.go @@ -1,8 +1,8 @@ /* * @Author: Vincent Yang * @Date: 2024-04-23 00:39:03 - * @LastEditors: Vincent Yang - * @LastEditTime: 2024-09-17 19:34:32 + * @LastEditors: Jason Lyu + * @LastEditTime: 2025-04-08 13:45:00 * @FilePath: /DeepLX/config.go * @Telegram: https://t.me/missuo * @GitHub: https://github.com/missuo @@ -10,7 +10,7 @@ * Copyright © 2024 by Vincent, All Rights Reserved. */ -package main +package service import ( "flag" @@ -26,7 +26,7 @@ type Config struct { Proxy string } -func initConfig() *Config { +func InitConfig() *Config { cfg := &Config{ IP: "0.0.0.0", Port: 1188, diff --git a/service/service.go b/service/service.go new file mode 100644 index 0000000..60a5062 --- /dev/null +++ b/service/service.go @@ -0,0 +1,275 @@ +/* + * @Author: Vincent Yang + * @Date: 2023-07-01 21:45:34 + * @LastEditors: Jason Lyu + * @LastEditTime: 2025-04-08 13:45:00 + * @FilePath: /DeepLX/main.go + * @Telegram: https://t.me/missuo + * @GitHub: https://github.com/missuo + * + * Copyright © 2024 by Vincent, All Rights Reserved. + */ + +package service + +import ( + "fmt" + "log" + "net/http" + "net/url" + "os" + "strings" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + + "github.com/OwO-Network/DeepLX/translate" +) + +func authMiddleware(cfg *Config) gin.HandlerFunc { + return func(c *gin.Context) { + if cfg.Token != "" { + providedTokenInQuery := c.Query("token") + providedTokenInHeader := c.GetHeader("Authorization") + + // Compatability with the Bearer token format + if providedTokenInHeader != "" { + parts := strings.Split(providedTokenInHeader, " ") + if len(parts) == 2 { + if parts[0] == "Bearer" || parts[0] == "DeepL-Auth-Key" { + providedTokenInHeader = parts[1] + } else { + providedTokenInHeader = "" + } + } else { + providedTokenInHeader = "" + } + } + + if providedTokenInHeader != cfg.Token && providedTokenInQuery != cfg.Token { + c.JSON(http.StatusUnauthorized, gin.H{ + "code": http.StatusUnauthorized, + "message": "Invalid access token", + }) + c.Abort() + return + } + } + + c.Next() + } +} + +type PayloadFree struct { + TransText string `json:"text"` + SourceLang string `json:"source_lang"` + TargetLang string `json:"target_lang"` + TagHandling string `json:"tag_handling"` +} + +type PayloadAPI struct { + Text []string `json:"text"` + TargetLang string `json:"target_lang"` + SourceLang string `json:"source_lang"` + TagHandling string `json:"tag_handling"` +} + +func Router(cfg *Config) *gin.Engine { + // Set Proxy + proxyURL := os.Getenv("PROXY") + if proxyURL == "" { + proxyURL = cfg.Proxy + } + if proxyURL != "" { + proxy, err := url.Parse(proxyURL) + if err != nil { + log.Fatalf("Failed to parse proxy URL: %v", err) + } + http.DefaultTransport = &http.Transport{ + Proxy: http.ProxyURL(proxy), + } + } + + if cfg.Token != "" { + fmt.Println("Access token is set.") + } + + r := gin.Default() + r.Use(cors.Default()) + + // Defining the root endpoint which returns the project details + r.GET("/", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusOK, + "message": "DeepL Free API, Developed by sjlleo and missuo. Go to /translate with POST. http://github.com/OwO-Network/DeepLX", + }) + }) + + // Free API endpoint, No Pro Account required + r.POST("/translate", authMiddleware(cfg), func(c *gin.Context) { + req := PayloadFree{} + c.BindJSON(&req) + + sourceLang := req.SourceLang + targetLang := req.TargetLang + translateText := req.TransText + tagHandling := req.TagHandling + + proxyURL := cfg.Proxy + + if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": http.StatusBadRequest, + "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.", + }) + return + } + + result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, "") + if err != nil { + log.Fatalf("Translation failed: %s", err) + } + + if result.Code == http.StatusOK { + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusOK, + "id": result.ID, + "data": result.Data, + "alternatives": result.Alternatives, + "source_lang": result.SourceLang, + "target_lang": result.TargetLang, + "method": result.Method, + }) + } else { + c.JSON(result.Code, gin.H{ + "code": result.Code, + "message": result.Message, + }) + + } + }) + + // Pro API endpoint, Pro Account required + r.POST("/v1/translate", authMiddleware(cfg), func(c *gin.Context) { + req := PayloadFree{} + c.BindJSON(&req) + + sourceLang := req.SourceLang + targetLang := req.TargetLang + translateText := req.TransText + tagHandling := req.TagHandling + proxyURL := cfg.Proxy + + dlSession := cfg.DlSession + + if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": http.StatusBadRequest, + "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.", + }) + return + } + + cookie := c.GetHeader("Cookie") + if cookie != "" { + dlSession = strings.Replace(cookie, "dl_session=", "", -1) + } + + if dlSession == "" { + c.JSON(http.StatusUnauthorized, gin.H{ + "code": http.StatusUnauthorized, + "message": "No dl_session Found", + }) + return + } else if strings.Contains(dlSession, ".") { + c.JSON(http.StatusUnauthorized, gin.H{ + "code": http.StatusUnauthorized, + "message": "Your account is not a Pro account. Please upgrade your account or switch to a different account.", + }) + return + } + + result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, dlSession) + if err != nil { + log.Fatalf("Translation failed: %s", err) + } + + if result.Code == http.StatusOK { + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusOK, + "id": result.ID, + "data": result.Data, + "alternatives": result.Alternatives, + "source_lang": result.SourceLang, + "target_lang": result.TargetLang, + "method": result.Method, + }) + } else { + c.JSON(result.Code, gin.H{ + "code": result.Code, + "message": result.Message, + }) + + } + }) + + // Free API endpoint, Consistent with the official API format + r.POST("/v2/translate", authMiddleware(cfg), func(c *gin.Context) { + proxyURL := cfg.Proxy + + var translateText string + var targetLang string + + translateText = c.PostForm("text") + targetLang = c.PostForm("target_lang") + + if translateText == "" || targetLang == "" { + var jsonData struct { + Text []string `json:"text"` + TargetLang string `json:"target_lang"` + } + + if err := c.BindJSON(&jsonData); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "code": http.StatusBadRequest, + "message": "Invalid request payload", + }) + return + } + + translateText = strings.Join(jsonData.Text, "\n") + targetLang = jsonData.TargetLang + } + + result, err := translate.TranslateByDeepLX("", targetLang, translateText, "", proxyURL, "") + if err != nil { + log.Fatalf("Translation failed: %s", err) + } + + if result.Code == http.StatusOK { + c.JSON(http.StatusOK, gin.H{ + "translations": []map[string]interface{}{ + { + "detected_source_language": result.SourceLang, + "text": result.Data, + }, + }, + }) + } else { + c.JSON(result.Code, gin.H{ + "code": result.Code, + "message": result.Message, + }) + } + }) + + // Catch-all route to handle undefined paths + r.NoRoute(func(c *gin.Context) { + c.JSON(http.StatusNotFound, gin.H{ + "code": http.StatusNotFound, + "message": "Path not found", + }) + }) + + return r +}