mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-08 14:18:28 +02:00
Added wip plugin store
- Added plugin store snippet - Added plugin list sync functions - Work in progress install / uninstall plugin function
This commit is contained in:
@@ -249,3 +249,5 @@ func (m *Manager) HandleDisablePlugin(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
||||
/* Plugin Store */
|
||||
|
@@ -82,7 +82,7 @@ func (m *Manager) LoadPluginsFromDisk() error {
|
||||
m.Log("Loaded plugin: "+thisPlugin.Spec.Name, nil)
|
||||
|
||||
// If the plugin was enabled, start it now
|
||||
fmt.Println("Plugin enabled state", m.GetPluginPreviousEnableState(thisPlugin.Spec.ID))
|
||||
//fmt.Println("Plugin enabled state", m.GetPluginPreviousEnableState(thisPlugin.Spec.ID))
|
||||
if m.GetPluginPreviousEnableState(thisPlugin.Spec.ID) {
|
||||
err = m.StartPlugin(thisPlugin.Spec.ID)
|
||||
if err != nil {
|
||||
|
118
src/mod/plugins/store.go
Normal file
118
src/mod/plugins/store.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"imuslab.com/zoraxy/mod/plugins/zoraxy_plugin"
|
||||
"imuslab.com/zoraxy/mod/utils"
|
||||
)
|
||||
|
||||
/*
|
||||
Plugin Store
|
||||
*/
|
||||
|
||||
// See https://github.com/aroz-online/zoraxy-official-plugins/blob/main/directories/index.json for the standard format
|
||||
|
||||
type Checksums struct {
|
||||
LinuxAmd64 string `json:"linux_amd64"`
|
||||
Linux386 string `json:"linux_386"`
|
||||
LinuxArm string `json:"linux_arm"`
|
||||
LinuxArm64 string `json:"linux_arm64"`
|
||||
LinuxMipsle string `json:"linux_mipsle"`
|
||||
LinuxRiscv64 string `json:"linux_riscv64"`
|
||||
WindowsAmd64 string `json:"windows_amd64"`
|
||||
}
|
||||
|
||||
type DownloadablePlugin struct {
|
||||
IconPath string
|
||||
PluginIntroSpect zoraxy_plugin.IntroSpect //Plugin introspect information
|
||||
ChecksumsSHA256 Checksums //Checksums for the plugin binary
|
||||
DownloadURLs map[string]string //Download URLs for different platforms
|
||||
}
|
||||
|
||||
/* Plugin Store Index List Sync */
|
||||
//Update the plugin list from the plugin store URLs
|
||||
func (m *Manager) UpdateDownloadablePluginList() error {
|
||||
//Get downloadable plugins from each of the plugin store URLS
|
||||
m.Options.DownloadablePluginCache = []*DownloadablePlugin{}
|
||||
for _, url := range m.Options.PluginStoreURLs {
|
||||
pluginList, err := m.getPluginListFromURL(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugin list from %s: %w", url, err)
|
||||
}
|
||||
m.Options.DownloadablePluginCache = append(m.Options.DownloadablePluginCache, pluginList...)
|
||||
}
|
||||
|
||||
m.Options.LastSuccPluginSyncTime = time.Now().Unix()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the plugin list from the URL
|
||||
func (m *Manager) getPluginListFromURL(url string) ([]*DownloadablePlugin, error) {
|
||||
//Get the plugin list from the URL
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to get plugin list from %s: %s", url, resp.Status)
|
||||
}
|
||||
|
||||
var pluginList []*DownloadablePlugin
|
||||
content, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read plugin list from %s: %w", url, err)
|
||||
}
|
||||
content = []byte(strings.TrimSpace(string(content)))
|
||||
|
||||
err = json.Unmarshal(content, &pluginList)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal plugin list from %s: %w", url, err)
|
||||
}
|
||||
|
||||
return pluginList, nil
|
||||
}
|
||||
|
||||
func (m *Manager) ListDownloadablePlugins() []*DownloadablePlugin {
|
||||
//List all downloadable plugins
|
||||
if len(m.Options.DownloadablePluginCache) == 0 {
|
||||
return []*DownloadablePlugin{}
|
||||
}
|
||||
return m.Options.DownloadablePluginCache
|
||||
}
|
||||
|
||||
/*
|
||||
Handlers for Plugin Store
|
||||
*/
|
||||
|
||||
func (m *Manager) HandleListDownloadablePlugins(w http.ResponseWriter, r *http.Request) {
|
||||
//List all downloadable plugins
|
||||
plugins := m.ListDownloadablePlugins()
|
||||
js, _ := json.Marshal(plugins)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
}
|
||||
|
||||
// HandleResyncPluginList is the handler for resyncing the plugin list from the plugin store URLs
|
||||
func (m *Manager) HandleResyncPluginList(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
//Make sure this function require csrf token
|
||||
utils.SendErrorResponse(w, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
//Resync the plugin list from the plugin store URLs
|
||||
err := m.UpdateDownloadablePluginList()
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Failed to resync plugin list: "+err.Error())
|
||||
return
|
||||
}
|
||||
utils.SendOK(w)
|
||||
}
|
52
src/mod/plugins/store_test.go
Normal file
52
src/mod/plugins/store_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpdateDownloadablePluginList(t *testing.T) {
|
||||
mockManager := &Manager{
|
||||
Options: &ManagerOptions{
|
||||
DownloadablePluginCache: []*DownloadablePlugin{},
|
||||
PluginStoreURLs: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
//Inject a mock URL for testing
|
||||
mockManager.Options.PluginStoreURLs = []string{"https://raw.githubusercontent.com/aroz-online/zoraxy-official-plugins/refs/heads/main/directories/index.json"}
|
||||
|
||||
err := mockManager.UpdateDownloadablePluginList()
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(mockManager.Options.DownloadablePluginCache) == 0 {
|
||||
t.Fatalf("expected plugin cache to be updated, but it was empty")
|
||||
}
|
||||
|
||||
if mockManager.Options.LastSuccPluginSyncTime == 0 {
|
||||
t.Fatalf("expected LastSuccPluginSyncTime to be updated, but it was not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPluginListFromURL(t *testing.T) {
|
||||
mockManager := &Manager{
|
||||
Options: &ManagerOptions{
|
||||
DownloadablePluginCache: []*DownloadablePlugin{},
|
||||
PluginStoreURLs: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
pluginList, err := mockManager.getPluginListFromURL("https://raw.githubusercontent.com/aroz-online/zoraxy-official-plugins/refs/heads/main/directories/index.json")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(pluginList) == 0 {
|
||||
t.Fatalf("expected plugin list to be populated, but it was empty")
|
||||
}
|
||||
|
||||
for _, plugin := range pluginList {
|
||||
t.Logf("Plugin: %+v", plugin)
|
||||
}
|
||||
}
|
@@ -29,10 +29,16 @@ type Plugin struct {
|
||||
}
|
||||
|
||||
type ManagerOptions struct {
|
||||
/* Plugins */
|
||||
PluginDir string //The directory where the plugins are stored
|
||||
PluginGroups map[string][]string //The plugin groups,key is the tag name and the value is an array of plugin IDs
|
||||
PluginGroupsConfig string //The group / tag configuration file, if set the plugin groups will be loaded from this file
|
||||
|
||||
/* Plugin Downloader */
|
||||
PluginStoreURLs []string //The plugin store URLs, used to download the plugins
|
||||
DownloadablePluginCache []*DownloadablePlugin //The cache for the downloadable plugins, key is the plugin ID and value is the DownloadablePlugin struct
|
||||
LastSuccPluginSyncTime int64 //The last sync time for the plugin store URLs, used to check if the plugin store URLs need to be synced again
|
||||
|
||||
/* Runtime */
|
||||
SystemConst *zoraxyPlugin.RuntimeConstantValue //The system constant value
|
||||
CSRFTokenGen func(*http.Request) string `json:"-"` //The CSRF token generator function
|
||||
|
Reference in New Issue
Block a user