jetbra-server-go 直接集成所有插件,并支持增量更新

感谢原作 @winffy2009jetbra-server-go 用golang 重复造个轮子吧
Update:
搞了个单 HTML 版,部署更方便了。
https://linux.do/t/topic/49380

原作者的服务只支持 IDE 本身的激活,想激活插件还有点麻烦。因此糊了一个自动拉取所有插件并展示到页面的能力,直接一个页面搞定所有插件。
每次进程重启的时候会自动拉取插件列表进行比对,有新的插件会自动加入。

// plugin.go,用于自动获取所有插件的 Code 信息,并写入 plugins.json 文件,方便下次启动直接加载
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"time"
)

const (
	pluginBaseUrl  = "https://plugins.jetbrains.com"
	pluginJsonFile = "plugins.json"
)

var (
	client = http.Client{
		Timeout: 60 * time.Second,
	}
	allPluginList []*Plugin
)

type ListPluginResponse struct {
	Plugins        []*Plugin `json:"plugins,omitempty"`
	Total          int       `json:"total,omitempty"`
	CorrectedQuery string    `json:"correctedQuery,omitempty"`
}

type Plugin struct {
	Id           int    `json:"id"`
	Code         string `json:"code,omitempty"`
	Name         string `json:"name"`
	PricingModel string `json:"pricingModel"`
	Icon         string `json:"icon"`
}

type PluginDetail struct {
	Id           int `json:"id"`
	PurchaseInfo struct {
		ProductCode   string      `json:"productCode"`
		BuyUrl        interface{} `json:"buyUrl"`
		PurchaseTerms interface{} `json:"purchaseTerms"`
		Optional      bool        `json:"optional"`
		TrialPeriod   int         `json:"trialPeriod"`
	} `json:"purchaseInfo"`
}

func init() {
	pluginFile, err := os.OpenFile(pluginJsonFile, os.O_RDONLY, 0644)
	if err == nil {
		err = json.NewDecoder(pluginFile).Decode(&allPluginList)
		if err != nil {
			panic(err)
		}
	}
	loadAllPlugin()
	savePlugin()
	return
}

func loadAllPlugin() {
	pluginIdCodeMap := make(map[int]string, len(allPluginList))
	for _, plugin := range allPluginList {
		pluginIdCodeMap[plugin.Id] = plugin.Code
	}

	pluginList, err := client.Get(pluginBaseUrl + "/api/searchPlugins?max=10000&offset=0")
	if err != nil {
		panic(err)
		return
	}
	defer pluginList.Body.Close()

	var listPluginResponse ListPluginResponse
	err = json.NewDecoder(pluginList.Body).Decode(&listPluginResponse)
	if err != nil {
		panic(err)
		return
	}

	for i, plugin := range listPluginResponse.Plugins {
		if plugin.PricingModel == "FREE" {
			continue
		}
		if pluginIdCodeMap[plugin.Id] != "" {
			continue
		}
		fmt.Println("found new plugin ", plugin.Name, plugin.PricingModel)
		listPluginResponse.Plugins[i].Icon = pluginBaseUrl + listPluginResponse.Plugins[i].Icon
		allPluginList = append(allPluginList, listPluginResponse.Plugins[i])
	}

	for _, plugin := range allPluginList {
		if plugin.Code == "" {
			plugin.Code = getCodeByPluginID(plugin.Id)
			fmt.Println("new plugin code ", plugin.Name, plugin.Code)
		}
	}
}

func getCodeByPluginID(id int) string {
	pluginDetailResp, err := client.Get(pluginBaseUrl + "/api/plugins/" + strconv.Itoa(id))
	if err != nil {
		panic(err)
	}
	defer pluginDetailResp.Body.Close()

	var pluginDetail PluginDetail
	err = json.NewDecoder(pluginDetailResp.Body).Decode(&pluginDetail)
	if err != nil {
		panic(err)
	}

	return pluginDetail.PurchaseInfo.ProductCode
}

func savePlugin() {
	f, err := os.Create(pluginJsonFile)
	if err != nil {
		panic(err)
	}
	err = json.NewEncoder(f).Encode(allPluginList)
	if err != nil {
		panic(err)
	}
}

main.go 里的修改,在渲染时将获取到的插件信息传入:

func index(c *gin.Context) {
	c.HTML(http.StatusOK, "/index.html", gin.H{
		"title":        "请选择",
		"licenseeName": "Evaluator",
		"assigneeName": "Evaluator",
		"expiryDate":   "2099-12-31",
		"plugins":      allPluginList,
	})
}

html 模板的修改,在最后一个 article 和 main 之间新增模板渲染:

    </article>
{{- range .plugins}}
    <article class="card" data-product="{{ .Name }}" data-product-codes="{{ .Code }}">
        <header>
            <div class="flex items-center justify-between px-6 pt-1 pb-0 bg-card radius-1">
                <div class="avatar-wrapper flex items-center justify-center overflow-hidden shrink-0">
                    <div class="icon" role="img" style="background-image: url('{{ .Icon }}')"></div>
                </div>
            </div>
            <hr/>
        </header>
        <div class="pd-6 overflow-hidden bg-card container radius-1">
            <h1 class="truncate truncate-1 color-primary mt-0 overflow-ellipsis" title='{{ .Name }}'>{{ .Name }}</h1>
            <p title="单据复制激活码到剪切板" class="truncate text-sm text-grey" onclick="copyLicense(this)"
               data-content="复制到剪切板">
                **********
            </p>
        </div>
        <div class="mask"></div>
        <div class="mask mask-c-1"></div>
    </article>
{{- end }}
</main>

改造后效果:

当前已获取到的完整 plugins.json,放根目录可以避免重复获取。
plugins.json.zip (7.2 KB)

84 个赞

赞赞赞

5 个赞

赞一个

5 个赞

mark

6 个赞

高级,插眼

5 个赞

马克

4 个赞

mark

4 个赞

万能的佬

4 个赞

Markkk!!

4 个赞

mark

4 个赞

求个开袋即食的docker镜像

5 个赞

mark!

4 个赞

mark

3 个赞

为啥没一个大佬发详细的使用教程,表示没用上啊,都不会用

3 个赞

佬,快写个详细教程,我是没看懂啊

4 个赞

mark一下

3 个赞

插眼,等着docker方案

3 个赞

mark

3 个赞

mark

3 个赞

更新是怎么触发的,还是没太明白

3 个赞