From 195c3ce20af91ebc707e3ec98acdcbbd4159ab8e Mon Sep 17 00:00:00 2001 From: linrenze <1210081098@qq.com> Date: Wed, 3 Dec 2025 19:16:08 +0800 Subject: [PATCH 1/4] add function to download jetbrains zip --- backend/internal/user/handler/v1/user.go | 80 ++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/backend/internal/user/handler/v1/user.go b/backend/internal/user/handler/v1/user.go index 2b356363..d254bfd6 100644 --- a/backend/internal/user/handler/v1/user.go +++ b/backend/internal/user/handler/v1/user.go @@ -40,6 +40,7 @@ type UserHandler struct { logger *slog.Logger cfg *config.Config vsixCache map[string]*CacheEntry + zipCache map[string]*CacheEntry cacheMu sync.RWMutex limiter *rate.Limiter } @@ -68,11 +69,14 @@ func NewUserHandler( logger: logger, cfg: cfg, vsixCache: make(map[string]*CacheEntry), + zipCache: make(map[string]*CacheEntry), limiter: rate.NewLimiter(rate.Every(time.Duration(cfg.Extension.LimitSecond)*time.Second), cfg.Extension.Limit), } w.GET("/api/v1/static/vsix/:version", web.BaseHandler(u.VSIXDownload)) w.GET("/api/v1/static/vsix", web.BaseHandler(u.VSIXDownload)) + w.GET("/api/v1/static/zip/:version", web.BaseHandler(u.ZipDownload)) + w.GET("/api/v1/static/zip", web.BaseHandler(u.ZipDownload)) w.POST("/api/v1/vscode/init-auth", web.BindHandler(u.VSCodeAuthInit)) // admin @@ -170,6 +174,13 @@ func (h *UserHandler) cleanExpiredCache() { delete(h.vsixCache, key) } } + if h.zipCache != nil { + for key, entry := range h.zipCache { + if now.Sub(entry.createdAt) > time.Hour { + delete(h.zipCache, key) + } + } + } } // VSIXDownload 下载VSCode插件 @@ -236,6 +247,75 @@ func (h *UserHandler) VSIXDownload(c *web.Context) error { return err } +// ZipDownload 下载ZIP包(Jetbrains插件包) +// +// @Tags User +// @Summary 下载ZIP包 +// @Description 下载ZIP安装包(缓存1小时) +// @ID zip-download +// @Accept json +// @Produce octet-stream +// @Router /api/v1/static/zip [get] +func (h *UserHandler) ZipDownload(c *web.Context) error { + if !h.limiter.Allow() { + return c.String(http.StatusTooManyRequests, "Too Many Requests") + } + + s, err := h.usecase.GetSetting(c.Request().Context()) + if err != nil { + return err + } + + host := c.Request().Host + h.logger.With("url", c.Request().URL).With("header", c.Request().Header).With("host", host).DebugContext(c.Request().Context(), "zip download") + cacheKey := h.generateCacheKey(version.Version, h.cfg.GetBaseURL(c.Request(), s)) + ver := strings.Trim(version.Version, "v") + + // 缓存命中 + h.cacheMu.RLock() + if entry, exists := h.zipCache[cacheKey]; exists { + if time.Since(entry.createdAt) < time.Hour { + h.cacheMu.RUnlock() + + disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.zip", ver) + c.Response().Header().Set("Content-Type", "application/octet-stream") + c.Response().Header().Set("Content-Disposition", disposition) + c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(entry.data))) + + fmt.Println(c.Response().Header()) + + _, werr := c.Response().Writer.Write(entry.data) + return werr + } + } + h.cacheMu.RUnlock() + + var buf bytes.Buffer + if err := vsix.ChangeVsixEndpoint(fmt.Sprintf("/app/assets/vsix/monkeycode-%s.zip", ver), "roo-code/package.json", h.cfg.GetBaseURL(c.Request(), s), &buf); err != nil { + return err + } + + data := buf.Bytes() + h.cacheMu.Lock() + h.zipCache[cacheKey] = &CacheEntry{ + data: data, + createdAt: time.Now(), + } + h.cacheMu.Unlock() + + // 异步清理 + go h.cleanExpiredCache() + + // 响应输出 + disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.zip", ver) + c.Response().Header().Set("Content-Type", "application/octet-stream") + c.Response().Header().Set("Content-Disposition", disposition) + c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(data))) + + _, err = c.Response().Writer.Write(data) + return err +} + // Login 用户登录 // // @Tags User From f340e50b19aaa12e4fbe6803adaa984981662010 Mon Sep 17 00:00:00 2001 From: linrenze <1210081098@qq.com> Date: Thu, 4 Dec 2025 14:31:27 +0800 Subject: [PATCH 2/4] feat: support backend download jetbrains zip --- .github/workflows/backend-ci-cd.yml | 3 +++ backend/build/Dockerfile | 1 + backend/internal/user/handler/v1/user.go | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backend-ci-cd.yml b/.github/workflows/backend-ci-cd.yml index a3f21b51..96f66381 100644 --- a/.github/workflows/backend-ci-cd.yml +++ b/.github/workflows/backend-ci-cd.yml @@ -99,6 +99,9 @@ jobs: VERSION_NO_V=${{ steps.get_version.outputs.VERSION }} VERSION_NO_V=${VERSION_NO_V#v} wget -O assets/vsix/monkeycode-${VERSION_NO_V}.vsix https://baizhiyun.oss-cn-hangzhou.aliyuncs.com/monkeycode/vsix/monkeycode-${VERSION_NO_V}.vsix + + # 下载 JetBrains 插件 (架构无关), jetbrains插件的下载地址硬编码, 后续需要修改 + wget -O assets/jetbrains/monkeycode-${VERSION_NO_V}.zip https://baizhiyun.oss-cn-hangzhou.aliyuncs.com/monkeycode/jetbrains/monkeycode-jetbrains-1.22.4.zip # 下载 x86_64 SGP wget -O tarballs/sgp.tgz https://baizhiyun.oss-cn-hangzhou.aliyuncs.com/monkeycode/sgp/x86_64/sgp.tgz diff --git a/backend/build/Dockerfile b/backend/build/Dockerfile index 4a768c8b..0fa22bbc 100644 --- a/backend/build/Dockerfile +++ b/backend/build/Dockerfile @@ -27,6 +27,7 @@ WORKDIR /app ADD migration ./migration ADD assets/vsix ./assets/vsix +ADD assets/jetbrains ./assets/jetbrains COPY --from=builder /out/main /app/main diff --git a/backend/internal/user/handler/v1/user.go b/backend/internal/user/handler/v1/user.go index d254bfd6..1886f7a9 100644 --- a/backend/internal/user/handler/v1/user.go +++ b/backend/internal/user/handler/v1/user.go @@ -291,7 +291,7 @@ func (h *UserHandler) ZipDownload(c *web.Context) error { h.cacheMu.RUnlock() var buf bytes.Buffer - if err := vsix.ChangeVsixEndpoint(fmt.Sprintf("/app/assets/vsix/monkeycode-%s.zip", ver), "roo-code/package.json", h.cfg.GetBaseURL(c.Request(), s), &buf); err != nil { + if err := vsix.ChangeVsixEndpoint(fmt.Sprintf("/app/assets/jetbrains/monkeycode-%s.zip", ver), "roo-code/package.json", h.cfg.GetBaseURL(c.Request(), s), &buf); err != nil { return err } From 2afcd50e6e978614774a4d00225a95eea38d088f Mon Sep 17 00:00:00 2001 From: linrenze <1210081098@qq.com> Date: Thu, 4 Dec 2025 18:55:58 +0800 Subject: [PATCH 3/4] change api description from zip to jetbrains --- backend/internal/user/handler/v1/user.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/internal/user/handler/v1/user.go b/backend/internal/user/handler/v1/user.go index 1886f7a9..54349b33 100644 --- a/backend/internal/user/handler/v1/user.go +++ b/backend/internal/user/handler/v1/user.go @@ -75,8 +75,8 @@ func NewUserHandler( w.GET("/api/v1/static/vsix/:version", web.BaseHandler(u.VSIXDownload)) w.GET("/api/v1/static/vsix", web.BaseHandler(u.VSIXDownload)) - w.GET("/api/v1/static/zip/:version", web.BaseHandler(u.ZipDownload)) - w.GET("/api/v1/static/zip", web.BaseHandler(u.ZipDownload)) + w.GET("/api/v1/static/jetbrains/:version", web.BaseHandler(u.ZipDownload)) + w.GET("/api/v1/static/jetbrains", web.BaseHandler(u.ZipDownload)) w.POST("/api/v1/vscode/init-auth", web.BindHandler(u.VSCodeAuthInit)) // admin From 5cdc231238209d2c93615568d5ad2f27d68fba27 Mon Sep 17 00:00:00 2001 From: linrenze <1210081098@qq.com> Date: Fri, 5 Dec 2025 12:55:26 +0800 Subject: [PATCH 4/4] rename Router --- backend/internal/user/handler/v1/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/user/handler/v1/user.go b/backend/internal/user/handler/v1/user.go index 54349b33..cc5efd8b 100644 --- a/backend/internal/user/handler/v1/user.go +++ b/backend/internal/user/handler/v1/user.go @@ -255,7 +255,7 @@ func (h *UserHandler) VSIXDownload(c *web.Context) error { // @ID zip-download // @Accept json // @Produce octet-stream -// @Router /api/v1/static/zip [get] +// @Router /api/v1/static/jetbrains [get] func (h *UserHandler) ZipDownload(c *web.Context) error { if !h.limiter.Allow() { return c.String(http.StatusTooManyRequests, "Too Many Requests")