Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
281
backend/sdk/cliproxy/home_plugins.go
Normal file
281
backend/sdk/cliproxy/home_plugins.go
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
package cliproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/home"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/homeplugins"
|
||||
sdkpluginstore "github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginstore"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const homePluginStatusReportTimeout = 10 * time.Second
|
||||
|
||||
type homePluginStatusWork struct {
|
||||
cfg *config.Config
|
||||
report homeplugins.SyncReport
|
||||
}
|
||||
|
||||
type homePluginTaskWork struct {
|
||||
cfg *config.Config
|
||||
task home.PluginTask
|
||||
report *homeplugins.SyncReport
|
||||
}
|
||||
|
||||
type homePluginFinalization struct {
|
||||
config *config.Config
|
||||
configCommit configCommit
|
||||
committed bool
|
||||
statusWork []homePluginStatusWork
|
||||
nextStatus int
|
||||
taskWork []homePluginTaskWork
|
||||
nextTask int
|
||||
syncKey string
|
||||
markSynced bool
|
||||
}
|
||||
|
||||
func (s *Service) syncHomePlugins(ctx context.Context, cfg *config.Config) (homeplugins.SyncReport, string, bool, error) {
|
||||
return s.syncHomePluginsWithClient(ctx, cfg, nil)
|
||||
}
|
||||
|
||||
func (s *Service) syncHomePluginsWithClient(ctx context.Context, cfg *config.Config, client *home.Client) (homeplugins.SyncReport, string, bool, error) {
|
||||
if s == nil || cfg == nil || !cfg.Home.Enabled {
|
||||
return homeplugins.SyncReport{}, "", false, nil
|
||||
}
|
||||
syncKey := homePluginSyncKey(cfg)
|
||||
if syncKey != "" {
|
||||
s.homePluginSyncMu.Lock()
|
||||
if s.homePluginSyncKey == syncKey {
|
||||
s.homePluginSyncMu.Unlock()
|
||||
return homeplugins.SyncReport{}, syncKey, false, nil
|
||||
}
|
||||
s.homePluginSyncMu.Unlock()
|
||||
}
|
||||
if !cfg.Plugins.Enabled {
|
||||
return homeplugins.CompletedSyncReport(homeplugins.CurrentPlatform(), nil), syncKey, false, nil
|
||||
}
|
||||
installedVersions, errInstalled := homeplugins.InstalledVersions(cfg)
|
||||
if errInstalled != nil {
|
||||
return homeplugins.CompletedSyncReport(homeplugins.CurrentPlatform(), errInstalled), syncKey, false, errInstalled
|
||||
}
|
||||
platform := homeplugins.CurrentPlatform()
|
||||
request := sdkpluginstore.PluginSyncRequest{
|
||||
SchemaVersion: sdkpluginstore.PluginSyncSchemaVersion,
|
||||
GOOS: platform.GOOS,
|
||||
GOARCH: platform.GOARCH,
|
||||
InstalledVersions: installedVersions,
|
||||
}
|
||||
defer request.Clear()
|
||||
response, errFetch := s.fetchHomePluginSyncWithClient(ctx, client, request)
|
||||
if errors.Is(errFetch, home.ErrPluginSyncUnsupported) {
|
||||
response.Clear()
|
||||
report, errSync := homeplugins.SyncWithReport(ctx, cfg, s.pluginHost)
|
||||
return report, syncKey, true, errSync
|
||||
}
|
||||
if errFetch != nil {
|
||||
return homeplugins.CompletedSyncReport(platform, errFetch), syncKey, false, errFetch
|
||||
}
|
||||
defer response.Clear()
|
||||
report, errSync := homeplugins.SyncResolvedWithReport(ctx, cfg, response.Items, response.ExpiresAt, request.InstalledVersions, s.pluginHost)
|
||||
return report, syncKey, true, errSync
|
||||
}
|
||||
|
||||
func (s *Service) fetchHomePluginSyncWithClient(ctx context.Context, client *home.Client, request sdkpluginstore.PluginSyncRequest) (sdkpluginstore.PluginSyncResponse, error) {
|
||||
if s.homePluginSyncFetch != nil {
|
||||
return s.homePluginSyncFetch(ctx, request)
|
||||
}
|
||||
if client == nil {
|
||||
s.homeMu.Lock()
|
||||
client = s.homeClient
|
||||
s.homeMu.Unlock()
|
||||
}
|
||||
if client == nil {
|
||||
return sdkpluginstore.PluginSyncResponse{}, fmt.Errorf("home client is unavailable")
|
||||
}
|
||||
return client.GetPluginSync(ctx, request)
|
||||
}
|
||||
|
||||
func (s *Service) markHomePluginsSynced(syncKey string) {
|
||||
if s == nil || strings.TrimSpace(syncKey) == "" {
|
||||
return
|
||||
}
|
||||
s.homePluginSyncMu.Lock()
|
||||
s.homePluginSyncKey = syncKey
|
||||
s.homePluginSyncMu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Service) reportHomePluginStatus(ctx context.Context, cfg *config.Config, report homeplugins.SyncReport) {
|
||||
s.reportHomePluginStatusWithClient(ctx, cfg, report, nil)
|
||||
}
|
||||
|
||||
func (s *Service) reportHomePluginStatusWithClient(ctx context.Context, cfg *config.Config, report homeplugins.SyncReport, client *home.Client) {
|
||||
if errReport := s.pushHomePluginStatusWithClient(ctx, cfg, report, client); errReport != nil {
|
||||
log.Warnf("failed to report home plugin status: %v", errReport)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) pushHomePluginStatusWithClient(ctx context.Context, cfg *config.Config, report homeplugins.SyncReport, client *home.Client) error {
|
||||
if s == nil || cfg == nil {
|
||||
return nil
|
||||
}
|
||||
if client == nil {
|
||||
s.homeMu.Lock()
|
||||
client = s.homeClient
|
||||
s.homeMu.Unlock()
|
||||
}
|
||||
if client == nil {
|
||||
return fmt.Errorf("home client is unavailable")
|
||||
}
|
||||
nodeID := strings.TrimSpace(cfg.Home.NodeID)
|
||||
if nodeID == "" {
|
||||
return fmt.Errorf("home node id is empty")
|
||||
}
|
||||
report.NodeID = nodeID
|
||||
report.UpdatedAt = time.Now().UTC()
|
||||
raw, errMarshal := json.Marshal(report)
|
||||
if errMarshal != nil {
|
||||
return fmt.Errorf("marshal home plugin status: %w", errMarshal)
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
reportCtx, cancel := context.WithTimeout(ctx, homePluginStatusReportTimeout)
|
||||
defer cancel()
|
||||
if errReport := client.RPushPluginStatus(reportCtx, raw); errReport != nil {
|
||||
return fmt.Errorf("push home plugin status: %w", errReport)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) processHomePluginTasks(ctx context.Context, cfg *config.Config) {
|
||||
s.processHomePluginTasksWithClient(ctx, cfg, nil)
|
||||
}
|
||||
|
||||
func (s *Service) processHomePluginTasksWithClient(ctx context.Context, cfg *config.Config, client *home.Client) {
|
||||
tasks, errStage := s.stageHomePluginTasksWithClient(ctx, cfg, client)
|
||||
if errStage != nil {
|
||||
log.Warnf("failed to fetch home plugin tasks: %v", errStage)
|
||||
return
|
||||
}
|
||||
work := &homePluginFinalization{taskWork: tasks}
|
||||
if errFinalize := s.finalizeHomePluginWork(ctx, client, work); errFinalize != nil {
|
||||
log.Warnf("failed to finalize home plugin tasks: %v", errFinalize)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) stageHomePluginTasksWithClient(ctx context.Context, cfg *config.Config, client *home.Client) ([]homePluginTaskWork, error) {
|
||||
if s == nil || cfg == nil || !cfg.Home.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
if client == nil {
|
||||
s.homeMu.Lock()
|
||||
client = s.homeClient
|
||||
s.homeMu.Unlock()
|
||||
}
|
||||
if client == nil {
|
||||
return nil, fmt.Errorf("home client is unavailable")
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
tasks, errTasks := client.GetPluginTasks(ctx)
|
||||
if errTasks != nil {
|
||||
return nil, errTasks
|
||||
}
|
||||
staged := make([]homePluginTaskWork, 0, len(tasks))
|
||||
for _, task := range tasks {
|
||||
if !strings.EqualFold(strings.TrimSpace(task.Operation), "delete") {
|
||||
continue
|
||||
}
|
||||
staged = append(staged, homePluginTaskWork{cfg: cfg, task: task})
|
||||
}
|
||||
return staged, nil
|
||||
}
|
||||
|
||||
func (s *Service) finalizeHomePluginWork(ctx context.Context, client *home.Client, work *homePluginFinalization) error {
|
||||
if work == nil {
|
||||
return nil
|
||||
}
|
||||
if ctx != nil {
|
||||
if errContext := ctx.Err(); errContext != nil {
|
||||
return errContext
|
||||
}
|
||||
}
|
||||
for work.nextStatus < len(work.statusWork) {
|
||||
status := work.statusWork[work.nextStatus]
|
||||
if errReport := s.pushHomePluginStatusWithClient(ctx, status.cfg, status.report, client); errReport != nil {
|
||||
return errReport
|
||||
}
|
||||
work.nextStatus++
|
||||
}
|
||||
for work.nextTask < len(work.taskWork) {
|
||||
taskWork := &work.taskWork[work.nextTask]
|
||||
if taskWork.report == nil {
|
||||
report := s.processHomePluginDeleteTask(ctx, taskWork.cfg, taskWork.task)
|
||||
taskWork.report = &report
|
||||
if !report.OK && strings.TrimSpace(report.Error) != "" {
|
||||
log.Warnf("failed to process home plugin delete task %d for %s: %v", taskWork.task.ID, taskWork.task.PluginID, report.Error)
|
||||
}
|
||||
}
|
||||
if errReport := s.pushHomePluginStatusWithClient(ctx, taskWork.cfg, *taskWork.report, client); errReport != nil {
|
||||
return errReport
|
||||
}
|
||||
work.nextTask++
|
||||
}
|
||||
if work.markSynced {
|
||||
if ctx != nil {
|
||||
if errContext := ctx.Err(); errContext != nil {
|
||||
return errContext
|
||||
}
|
||||
}
|
||||
s.markHomePluginsSynced(work.syncKey)
|
||||
work.markSynced = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) processHomePluginDeleteTask(ctx context.Context, cfg *config.Config, task home.PluginTask) homeplugins.SyncReport {
|
||||
if s != nil && s.homePluginDeleteTask != nil {
|
||||
return s.homePluginDeleteTask(ctx, cfg, task)
|
||||
}
|
||||
return homeplugins.DeleteWithReport(ctx, cfg, s.pluginHost, task.ID, task.PluginID)
|
||||
}
|
||||
|
||||
func homePluginSyncKey(cfg *config.Config) string {
|
||||
if cfg == nil || !cfg.Home.Enabled {
|
||||
return ""
|
||||
}
|
||||
hash := sha256.New()
|
||||
_, _ = fmt.Fprintf(hash, "enabled=%t\ndir=%s\nauth-revision=%d\n", cfg.Plugins.Enabled, strings.TrimSpace(cfg.Plugins.Dir), cfg.Plugins.AuthRevision)
|
||||
ids := make([]string, 0, len(cfg.Plugins.Configs))
|
||||
for id := range cfg.Plugins.Configs {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
for _, id := range ids {
|
||||
item := cfg.Plugins.Configs[id]
|
||||
enabled := false
|
||||
if item.Enabled != nil {
|
||||
enabled = *item.Enabled
|
||||
}
|
||||
_, _ = fmt.Fprintf(hash, "plugin=%s\nenabled=%t\npriority=%d\n", strings.TrimSpace(id), enabled, item.Priority)
|
||||
if item.Raw.Kind != 0 {
|
||||
raw, errMarshal := yaml.Marshal(&item.Raw)
|
||||
if errMarshal == nil {
|
||||
_, _ = hash.Write(raw)
|
||||
}
|
||||
}
|
||||
_, _ = hash.Write([]byte{'\n'})
|
||||
}
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
Loading…
Reference in a new issue