mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-10-13 14:19:43 +02:00
215 lines
5.9 KiB
Go
215 lines
5.9 KiB
Go
package plugins
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"imuslab.com/zoraxy/mod/utils"
|
|
)
|
|
|
|
// StartHotReloadTicker starts the hot reload ticker
|
|
func (m *Manager) StartHotReloadTicker() error {
|
|
if m.pluginReloadTicker != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Hot reload ticker already started", nil)
|
|
return errors.New("hot reload ticker already started")
|
|
}
|
|
|
|
m.pluginReloadTicker = time.NewTicker(time.Duration(m.Options.HotReloadInterval) * time.Second)
|
|
m.pluginReloadStop = make(chan bool)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-m.pluginReloadTicker.C:
|
|
err := m.UpdatePluginHashList(false)
|
|
if err != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Failed to update plugin hash list", err)
|
|
}
|
|
case <-m.pluginReloadStop:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Hot reload ticker started", nil)
|
|
return nil
|
|
|
|
}
|
|
|
|
// StopHotReloadTicker stops the hot reload ticker
|
|
func (m *Manager) StopHotReloadTicker() error {
|
|
if m.pluginReloadTicker != nil {
|
|
m.pluginReloadStop <- true
|
|
m.pluginReloadTicker.Stop()
|
|
m.pluginReloadTicker = nil
|
|
m.pluginReloadStop = nil
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Hot reload ticker stopped", nil)
|
|
} else {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Hot reload ticker already stopped", nil)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *Manager) InitPluginHashList() error {
|
|
return m.UpdatePluginHashList(true)
|
|
}
|
|
|
|
// Update the plugin hash list and if there are change, reload the plugin
|
|
func (m *Manager) UpdatePluginHashList(noReload bool) error {
|
|
for pluginId, plugin := range m.LoadedPlugins {
|
|
//Get the plugin Entry point
|
|
pluginEntryPoint, err := m.GetPluginEntryPoint(plugin.RootDir)
|
|
if err != nil {
|
|
//Unable to get the entry point of the plugin
|
|
return err
|
|
}
|
|
|
|
file, err := os.Open(pluginEntryPoint)
|
|
if err != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Failed to open plugin entry point: "+pluginEntryPoint, err)
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
//Calculate the hash of the file
|
|
hasher := sha256.New()
|
|
if _, err := file.Seek(0, 0); err != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Failed to seek plugin entry point: "+pluginEntryPoint, err)
|
|
return err
|
|
}
|
|
if _, err := io.Copy(hasher, file); err != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Failed to copy plugin entry point: "+pluginEntryPoint, err)
|
|
return err
|
|
}
|
|
hash := hex.EncodeToString(hasher.Sum(nil))
|
|
m.pluginCheckMutex.Lock()
|
|
if m.PluginHash[pluginId] != hash {
|
|
m.PluginHash[pluginId] = hash
|
|
m.pluginCheckMutex.Unlock()
|
|
if !noReload {
|
|
//Plugin file changed, reload the plugin
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Plugin file changed, reloading plugin: "+pluginId, nil)
|
|
err := m.HotReloadPlugin(pluginId)
|
|
if err != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Failed to reload plugin: "+pluginId, err)
|
|
return err
|
|
} else {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Plugin reloaded: "+pluginId, nil)
|
|
}
|
|
} else {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Plugin hash generated for: "+pluginId, nil)
|
|
}
|
|
} else {
|
|
m.pluginCheckMutex.Unlock()
|
|
}
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Reload the plugin from file system
|
|
func (m *Manager) HotReloadPlugin(pluginId string) error {
|
|
//Check if the plugin is currently running
|
|
thisPlugin, err := m.GetPluginByID(pluginId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if thisPlugin.IsRunning() {
|
|
err = m.StopPlugin(pluginId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
//Remove the plugin from the loaded plugins list
|
|
m.loadedPluginsMutex.Lock()
|
|
if _, ok := m.LoadedPlugins[pluginId]; ok {
|
|
delete(m.LoadedPlugins, pluginId)
|
|
} else {
|
|
m.loadedPluginsMutex.Unlock()
|
|
return nil
|
|
}
|
|
m.loadedPluginsMutex.Unlock()
|
|
|
|
//Reload the plugin from disk, it should reload the plugin from latest version
|
|
m.ReloadPluginFromDisk()
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
Request handlers for developer options
|
|
*/
|
|
func (m *Manager) HandleEnableHotReload(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == http.MethodGet {
|
|
//Return the current status of hot reload
|
|
js, _ := json.Marshal(m.Options.EnableHotReload)
|
|
utils.SendJSONResponse(w, string(js))
|
|
return
|
|
}
|
|
|
|
enabled, err := utils.PostBool(r, "enabled")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "enabled not found")
|
|
return
|
|
}
|
|
m.Options.EnableHotReload = enabled
|
|
if enabled {
|
|
//Start the hot reload ticker
|
|
err := m.StartHotReloadTicker()
|
|
if err != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Failed to start hot reload ticker", err)
|
|
utils.SendErrorResponse(w, "Failed to start hot reload ticker")
|
|
return
|
|
}
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Hot reload enabled", nil)
|
|
} else {
|
|
//Stop the hot reload ticker
|
|
err := m.StopHotReloadTicker()
|
|
if err != nil {
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Failed to stop hot reload ticker", err)
|
|
utils.SendErrorResponse(w, "Failed to stop hot reload ticker")
|
|
return
|
|
}
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Hot reload disabled", nil)
|
|
}
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func (m *Manager) HandleSetHotReloadInterval(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == http.MethodGet {
|
|
//Return the current status of hot reload
|
|
js, _ := json.Marshal(m.Options.HotReloadInterval)
|
|
utils.SendJSONResponse(w, string(js))
|
|
return
|
|
}
|
|
|
|
interval, err := utils.PostInt(r, "interval")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "interval not found")
|
|
return
|
|
}
|
|
|
|
if interval < 1 {
|
|
utils.SendErrorResponse(w, "interval must be at least 1 second")
|
|
return
|
|
}
|
|
m.Options.HotReloadInterval = interval
|
|
|
|
//Restart the hot reload ticker
|
|
if m.pluginReloadTicker != nil {
|
|
m.StopHotReloadTicker()
|
|
time.Sleep(1 * time.Second)
|
|
//Start the hot reload ticker again
|
|
m.StartHotReloadTicker()
|
|
}
|
|
m.Options.Logger.PrintAndLog("plugin-manager", "Hot reload interval set to "+strconv.Itoa(interval)+" sec", nil)
|
|
utils.SendOK(w)
|
|
}
|