Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
652
backend/internal/pluginhost/auth_callbacks.go
Normal file
652
backend/internal/pluginhost/auth_callbacks.go
Normal file
|
|
@ -0,0 +1,652 @@
|
|||
package pluginhost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginapi"
|
||||
)
|
||||
|
||||
type rpcHostAuthGetRequest struct {
|
||||
AuthIndex string `json:"auth_index"`
|
||||
}
|
||||
|
||||
type rpcHostAuthListResponse struct {
|
||||
Files []pluginapi.HostAuthFileEntry `json:"files"`
|
||||
}
|
||||
|
||||
type rpcHostAuthGetResponse struct {
|
||||
AuthIndex string `json:"auth_index"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
JSON json.RawMessage `json:"json"`
|
||||
}
|
||||
|
||||
func (h *Host) SetAuthManager(manager *coreauth.Manager) {
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
h.mu.Lock()
|
||||
h.authManager = manager
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
func (h *Host) currentAuthManager() *coreauth.Manager {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
h.mu.Lock()
|
||||
manager := h.authManager
|
||||
h.mu.Unlock()
|
||||
return manager
|
||||
}
|
||||
|
||||
func (h *Host) callHostAuthList(ctx context.Context, request []byte) ([]byte, error) {
|
||||
_ = ctx
|
||||
if len(bytesTrimSpace(request)) > 0 {
|
||||
var req map[string]any
|
||||
if errUnmarshal := json.Unmarshal(request, &req); errUnmarshal != nil {
|
||||
return nil, fmt.Errorf("decode host auth list request: %w", errUnmarshal)
|
||||
}
|
||||
}
|
||||
entries, errList := h.listAuthFiles()
|
||||
if errList != nil {
|
||||
return nil, errList
|
||||
}
|
||||
return marshalRPCResult(rpcHostAuthListResponse{Files: entries})
|
||||
}
|
||||
|
||||
func (h *Host) callHostAuthGet(ctx context.Context, request []byte) ([]byte, error) {
|
||||
_ = ctx
|
||||
var req rpcHostAuthGetRequest
|
||||
if errUnmarshal := json.Unmarshal(request, &req); errUnmarshal != nil {
|
||||
return nil, fmt.Errorf("decode host auth get request: %w", errUnmarshal)
|
||||
}
|
||||
authIndex := strings.TrimSpace(req.AuthIndex)
|
||||
if authIndex == "" {
|
||||
return nil, fmt.Errorf("auth_index is required")
|
||||
}
|
||||
auth, rawJSON, errGet := h.authPhysicalJSONByIndex(authIndex)
|
||||
if errGet != nil {
|
||||
return nil, errGet
|
||||
}
|
||||
name := strings.TrimSpace(auth.FileName)
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(auth.ID)
|
||||
}
|
||||
path := strings.TrimSpace(authAttribute(auth, "path"))
|
||||
return marshalRPCResult(rpcHostAuthGetResponse{
|
||||
AuthIndex: authIndex,
|
||||
Name: name,
|
||||
Path: path,
|
||||
JSON: json.RawMessage(rawJSON),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Host) callHostAuthGetRuntime(ctx context.Context, request []byte) ([]byte, error) {
|
||||
_ = ctx
|
||||
var req rpcHostAuthGetRequest
|
||||
if errUnmarshal := json.Unmarshal(request, &req); errUnmarshal != nil {
|
||||
return nil, fmt.Errorf("decode host auth get runtime request: %w", errUnmarshal)
|
||||
}
|
||||
authIndex := strings.TrimSpace(req.AuthIndex)
|
||||
if authIndex == "" {
|
||||
return nil, fmt.Errorf("auth_index is required")
|
||||
}
|
||||
auth, errGet := h.authByIndex(authIndex)
|
||||
if errGet != nil {
|
||||
return nil, errGet
|
||||
}
|
||||
entry := h.buildHostAuthFileEntry(auth)
|
||||
if entry == nil {
|
||||
return nil, fmt.Errorf("auth runtime info not found for auth_index %s", authIndex)
|
||||
}
|
||||
return marshalRPCResult(pluginapi.HostAuthGetRuntimeResponse{Auth: *entry})
|
||||
}
|
||||
|
||||
func (h *Host) callHostAuthSave(ctx context.Context, request []byte) ([]byte, error) {
|
||||
var req pluginapi.HostAuthSaveRequest
|
||||
if errUnmarshal := json.Unmarshal(request, &req); errUnmarshal != nil {
|
||||
return nil, fmt.Errorf("decode host auth save request: %w", errUnmarshal)
|
||||
}
|
||||
name, rawJSON, errValidate := validateHostAuthSaveRequest(req)
|
||||
if errValidate != nil {
|
||||
return nil, errValidate
|
||||
}
|
||||
path, errSave := h.saveAuthFile(ctx, name, rawJSON)
|
||||
if errSave != nil {
|
||||
return nil, errSave
|
||||
}
|
||||
return marshalRPCResult(pluginapi.HostAuthSaveResponse{
|
||||
Name: name,
|
||||
Path: path,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Host) listAuthFiles() ([]pluginapi.HostAuthFileEntry, error) {
|
||||
manager := h.currentAuthManager()
|
||||
if manager != nil {
|
||||
auths := manager.List()
|
||||
entries := make([]pluginapi.HostAuthFileEntry, 0, len(auths))
|
||||
for _, auth := range auths {
|
||||
if entry := h.buildHostAuthFileEntry(auth); entry != nil {
|
||||
entries = append(entries, *entry)
|
||||
}
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return strings.ToLower(entries[i].Name) < strings.ToLower(entries[j].Name)
|
||||
})
|
||||
return entries, nil
|
||||
}
|
||||
return h.listAuthFilesFromDisk()
|
||||
}
|
||||
|
||||
func (h *Host) listAuthFilesFromDisk() ([]pluginapi.HostAuthFileEntry, error) {
|
||||
authDir := h.resolvedAuthDir()
|
||||
if authDir == "" {
|
||||
return nil, fmt.Errorf("auth directory is unavailable")
|
||||
}
|
||||
entries, errReadDir := os.ReadDir(authDir)
|
||||
if errReadDir != nil {
|
||||
return nil, fmt.Errorf("failed to read auth dir: %w", errReadDir)
|
||||
}
|
||||
files := make([]pluginapi.HostAuthFileEntry, 0)
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if !strings.HasSuffix(strings.ToLower(name), ".json") {
|
||||
continue
|
||||
}
|
||||
full := filepath.Join(authDir, name)
|
||||
fileEntry := pluginapi.HostAuthFileEntry{
|
||||
Name: name,
|
||||
Source: "file",
|
||||
Path: full,
|
||||
}
|
||||
if info, errInfo := entry.Info(); errInfo == nil {
|
||||
fileEntry.Size = info.Size()
|
||||
fileEntry.ModTime = info.ModTime()
|
||||
}
|
||||
if data, errRead := os.ReadFile(full); errRead == nil {
|
||||
var metadata map[string]any
|
||||
if errUnmarshal := json.Unmarshal(data, &metadata); errUnmarshal == nil {
|
||||
if provider, ok := metadata["type"].(string); ok {
|
||||
fileEntry.Type = strings.TrimSpace(provider)
|
||||
fileEntry.Provider = fileEntry.Type
|
||||
}
|
||||
if email, ok := metadata["email"].(string); ok {
|
||||
fileEntry.Email = strings.TrimSpace(email)
|
||||
}
|
||||
if projectID, ok := metadata["project_id"].(string); ok {
|
||||
fileEntry.ProjectID = strings.TrimSpace(projectID)
|
||||
}
|
||||
if rawPriority, ok := metadata["priority"]; ok {
|
||||
if priority, okPriority := parsePriorityValue(rawPriority); okPriority {
|
||||
fileEntry.Priority = priority
|
||||
}
|
||||
}
|
||||
if note, ok := metadata["note"].(string); ok {
|
||||
fileEntry.Note = strings.TrimSpace(note)
|
||||
}
|
||||
if websockets, okWebsockets := parseWebsocketsValue(metadata["websockets"]); okWebsockets {
|
||||
fileEntry.Websockets = websockets
|
||||
}
|
||||
}
|
||||
}
|
||||
files = append(files, fileEntry)
|
||||
}
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
return strings.ToLower(files[i].Name) < strings.ToLower(files[j].Name)
|
||||
})
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (h *Host) authByIndex(authIndex string) (*coreauth.Auth, error) {
|
||||
authIndex = strings.TrimSpace(authIndex)
|
||||
if authIndex == "" {
|
||||
return nil, fmt.Errorf("auth_index is required")
|
||||
}
|
||||
manager := h.currentAuthManager()
|
||||
if manager == nil {
|
||||
return nil, fmt.Errorf("core auth manager unavailable")
|
||||
}
|
||||
for _, auth := range manager.List() {
|
||||
if auth == nil {
|
||||
continue
|
||||
}
|
||||
auth.EnsureIndex()
|
||||
if auth.Index == authIndex {
|
||||
return auth, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("auth not found for auth_index %s", authIndex)
|
||||
}
|
||||
|
||||
func (h *Host) authPhysicalJSONByIndex(authIndex string) (*coreauth.Auth, []byte, error) {
|
||||
auth, errGet := h.authByIndex(authIndex)
|
||||
if errGet != nil {
|
||||
return nil, nil, errGet
|
||||
}
|
||||
path := strings.TrimSpace(authAttribute(auth, "path"))
|
||||
if path == "" {
|
||||
return nil, nil, fmt.Errorf("auth file path not found for auth_index %s", authIndex)
|
||||
}
|
||||
data, errRead := os.ReadFile(path)
|
||||
if errRead != nil {
|
||||
if os.IsNotExist(errRead) {
|
||||
return nil, nil, fmt.Errorf("auth file not found for auth_index %s", authIndex)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to read auth file: %w", errRead)
|
||||
}
|
||||
if len(bytesTrimSpace(data)) == 0 {
|
||||
return nil, nil, fmt.Errorf("auth file is empty for auth_index %s", authIndex)
|
||||
}
|
||||
var metadata map[string]any
|
||||
if errUnmarshal := json.Unmarshal(data, &metadata); errUnmarshal != nil {
|
||||
return nil, nil, fmt.Errorf("invalid auth file for auth_index %s: %w", authIndex, errUnmarshal)
|
||||
}
|
||||
return auth, data, nil
|
||||
}
|
||||
|
||||
func validateHostAuthSaveRequest(req pluginapi.HostAuthSaveRequest) (string, []byte, error) {
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if isUnsafeAuthFileName(name) {
|
||||
return "", nil, fmt.Errorf("invalid auth file name")
|
||||
}
|
||||
if !strings.HasSuffix(strings.ToLower(name), ".json") {
|
||||
return "", nil, fmt.Errorf("auth file name must end with .json")
|
||||
}
|
||||
rawJSON := bytesTrimSpace(req.JSON)
|
||||
if len(rawJSON) == 0 {
|
||||
return "", nil, fmt.Errorf("json is required")
|
||||
}
|
||||
var metadata map[string]any
|
||||
if errUnmarshal := json.Unmarshal(rawJSON, &metadata); errUnmarshal != nil {
|
||||
return "", nil, fmt.Errorf("invalid auth json: %w", errUnmarshal)
|
||||
}
|
||||
return filepath.Base(name), rawJSON, nil
|
||||
}
|
||||
|
||||
func (h *Host) saveAuthFile(ctx context.Context, name string, data []byte) (string, error) {
|
||||
authDir := h.resolvedAuthDir()
|
||||
if authDir == "" {
|
||||
return "", fmt.Errorf("auth directory is unavailable")
|
||||
}
|
||||
dst := filepath.Join(authDir, filepath.Base(name))
|
||||
if !filepath.IsAbs(dst) {
|
||||
if abs, errAbs := filepath.Abs(dst); errAbs == nil {
|
||||
dst = abs
|
||||
}
|
||||
}
|
||||
auth, errBuild := h.buildAuthFromFileData(dst, data)
|
||||
if errBuild != nil {
|
||||
return "", errBuild
|
||||
}
|
||||
if errWrite := os.WriteFile(dst, data, 0o600); errWrite != nil {
|
||||
return "", fmt.Errorf("failed to write auth file: %w", errWrite)
|
||||
}
|
||||
if errUpsert := h.upsertAuthRecord(ctx, auth); errUpsert != nil {
|
||||
return "", errUpsert
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func (h *Host) buildAuthFromFileData(path string, data []byte) (*coreauth.Auth, error) {
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return nil, fmt.Errorf("auth path is empty")
|
||||
}
|
||||
if data == nil {
|
||||
var errRead error
|
||||
data, errRead = os.ReadFile(path)
|
||||
if errRead != nil {
|
||||
return nil, fmt.Errorf("failed to read auth file: %w", errRead)
|
||||
}
|
||||
}
|
||||
metadata := make(map[string]any)
|
||||
if errUnmarshal := json.Unmarshal(data, &metadata); errUnmarshal != nil {
|
||||
return nil, fmt.Errorf("invalid auth file: %w", errUnmarshal)
|
||||
}
|
||||
coreauth.NormalizeCredentialMetadata(metadata)
|
||||
provider, _ := metadata["type"].(string)
|
||||
if strings.TrimSpace(provider) == "" {
|
||||
provider = "unknown"
|
||||
}
|
||||
label := provider
|
||||
if email, ok := metadata["email"].(string); ok && strings.TrimSpace(email) != "" {
|
||||
label = strings.TrimSpace(email)
|
||||
}
|
||||
authID := h.authIDForPath(path)
|
||||
if authID == "" {
|
||||
authID = path
|
||||
}
|
||||
auth := &coreauth.Auth{
|
||||
ID: authID,
|
||||
Provider: provider,
|
||||
FileName: filepath.Base(path),
|
||||
Label: label,
|
||||
Status: coreauth.StatusActive,
|
||||
Attributes: map[string]string{
|
||||
"path": path,
|
||||
"source": path,
|
||||
},
|
||||
Metadata: metadata,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}
|
||||
if manager := h.currentAuthManager(); manager != nil {
|
||||
if existing, ok := manager.GetByID(authID); ok {
|
||||
auth.CreatedAt = existing.CreatedAt
|
||||
auth.LastRefreshedAt = existing.LastRefreshedAt
|
||||
auth.NextRetryAfter = existing.NextRetryAfter
|
||||
auth.Runtime = existing.Runtime
|
||||
}
|
||||
}
|
||||
if errWeight := coreauth.ValidateAuthWeight(auth); errWeight != nil {
|
||||
return nil, fmt.Errorf("invalid auth weight: %w", errWeight)
|
||||
}
|
||||
coreauth.ApplyCustomHeadersFromMetadata(auth)
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func (h *Host) upsertAuthRecord(ctx context.Context, auth *coreauth.Auth) error {
|
||||
manager := h.currentAuthManager()
|
||||
if manager == nil || auth == nil {
|
||||
return nil
|
||||
}
|
||||
if existing, ok := manager.GetByID(auth.ID); ok {
|
||||
auth.CreatedAt = existing.CreatedAt
|
||||
_, errUpdate := manager.Update(ctx, auth)
|
||||
return errUpdate
|
||||
}
|
||||
_, errRegister := manager.Register(ctx, auth)
|
||||
return errRegister
|
||||
}
|
||||
|
||||
func isUnsafeAuthFileName(name string) bool {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return true
|
||||
}
|
||||
if strings.ContainsAny(name, "/\\") {
|
||||
return true
|
||||
}
|
||||
if filepath.VolumeName(name) != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *Host) buildHostAuthFileEntry(auth *coreauth.Auth) *pluginapi.HostAuthFileEntry {
|
||||
if auth == nil {
|
||||
return nil
|
||||
}
|
||||
auth.EnsureIndex()
|
||||
runtimeOnly := isRuntimeOnlyAuth(auth)
|
||||
if runtimeOnly && (auth.Disabled || auth.Status == coreauth.StatusDisabled) {
|
||||
return nil
|
||||
}
|
||||
path := strings.TrimSpace(authAttribute(auth, "path"))
|
||||
if path == "" && !runtimeOnly {
|
||||
return nil
|
||||
}
|
||||
name := strings.TrimSpace(auth.FileName)
|
||||
if name == "" {
|
||||
name = auth.ID
|
||||
}
|
||||
entry := &pluginapi.HostAuthFileEntry{
|
||||
ID: auth.ID,
|
||||
AuthIndex: auth.Index,
|
||||
Name: name,
|
||||
Type: strings.TrimSpace(auth.Provider),
|
||||
Provider: strings.TrimSpace(auth.Provider),
|
||||
Label: auth.Label,
|
||||
Status: string(auth.Status),
|
||||
StatusMessage: auth.StatusMessage,
|
||||
Disabled: auth.Disabled,
|
||||
Unavailable: auth.Unavailable,
|
||||
RuntimeOnly: runtimeOnly,
|
||||
Source: "memory",
|
||||
Success: auth.Success,
|
||||
Failed: auth.Failed,
|
||||
RecentRequests: hostRecentRequests(auth),
|
||||
}
|
||||
if email := authEmail(auth); email != "" {
|
||||
entry.Email = email
|
||||
}
|
||||
if projectID := authProjectID(auth); projectID != "" {
|
||||
entry.ProjectID = projectID
|
||||
}
|
||||
if accountType, account := auth.AccountInfo(); accountType != "" || account != "" {
|
||||
entry.AccountType = accountType
|
||||
entry.Account = account
|
||||
}
|
||||
if !auth.CreatedAt.IsZero() {
|
||||
entry.CreatedAt = auth.CreatedAt
|
||||
}
|
||||
if !auth.UpdatedAt.IsZero() {
|
||||
entry.ModTime = auth.UpdatedAt
|
||||
entry.UpdatedAt = auth.UpdatedAt
|
||||
}
|
||||
if !auth.LastRefreshedAt.IsZero() {
|
||||
entry.LastRefresh = auth.LastRefreshedAt
|
||||
}
|
||||
if !auth.NextRetryAfter.IsZero() {
|
||||
entry.NextRetryAfter = auth.NextRetryAfter
|
||||
}
|
||||
if path != "" {
|
||||
entry.Path = path
|
||||
entry.Source = "file"
|
||||
if info, err := os.Stat(path); err == nil {
|
||||
entry.Size = info.Size()
|
||||
entry.ModTime = info.ModTime()
|
||||
} else if os.IsNotExist(err) {
|
||||
if !runtimeOnly && (auth.Disabled || auth.Status == coreauth.StatusDisabled || strings.EqualFold(strings.TrimSpace(auth.StatusMessage), "removed via management api")) {
|
||||
return nil
|
||||
}
|
||||
entry.Source = "memory"
|
||||
}
|
||||
}
|
||||
if p := strings.TrimSpace(authAttribute(auth, "priority")); p != "" {
|
||||
if parsed, err := strconv.Atoi(p); err == nil {
|
||||
entry.Priority = parsed
|
||||
}
|
||||
} else if auth.Metadata != nil {
|
||||
if rawPriority, ok := auth.Metadata["priority"]; ok {
|
||||
if priority, okPriority := parsePriorityValue(rawPriority); okPriority {
|
||||
entry.Priority = priority
|
||||
}
|
||||
}
|
||||
}
|
||||
if note := strings.TrimSpace(authAttribute(auth, "note")); note != "" {
|
||||
entry.Note = note
|
||||
} else if auth.Metadata != nil {
|
||||
if rawNote, ok := auth.Metadata["note"].(string); ok {
|
||||
entry.Note = strings.TrimSpace(rawNote)
|
||||
}
|
||||
}
|
||||
if websockets, ok := authWebsocketsValue(auth); ok {
|
||||
entry.Websockets = websockets
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
func (h *Host) resolvedAuthDir() string {
|
||||
if h == nil {
|
||||
return ""
|
||||
}
|
||||
h.mu.Lock()
|
||||
authDir := ""
|
||||
if h.runtimeConfig != nil {
|
||||
authDir = strings.TrimSpace(h.runtimeConfig.AuthDir)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
if authDir == "" {
|
||||
return ""
|
||||
}
|
||||
authDir = filepath.Clean(authDir)
|
||||
if !filepath.IsAbs(authDir) {
|
||||
if abs, errAbs := filepath.Abs(authDir); errAbs == nil {
|
||||
authDir = abs
|
||||
}
|
||||
}
|
||||
return authDir
|
||||
}
|
||||
|
||||
func (h *Host) authIDForPath(path string) string {
|
||||
path = strings.TrimSpace(path)
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
path = filepath.Clean(path)
|
||||
if !filepath.IsAbs(path) {
|
||||
if abs, errAbs := filepath.Abs(path); errAbs == nil {
|
||||
path = abs
|
||||
}
|
||||
}
|
||||
id := path
|
||||
if authDir := h.resolvedAuthDir(); authDir != "" {
|
||||
if rel, errRel := filepath.Rel(authDir, path); errRel == nil && rel != "" {
|
||||
id = rel
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
id = strings.ToLower(id)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func authEmail(auth *coreauth.Auth) string {
|
||||
if auth == nil {
|
||||
return ""
|
||||
}
|
||||
if auth.Metadata != nil {
|
||||
if v, ok := auth.Metadata["email"].(string); ok {
|
||||
return strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
if auth.Attributes != nil {
|
||||
if v := strings.TrimSpace(auth.Attributes["email"]); v != "" {
|
||||
return v
|
||||
}
|
||||
if v := strings.TrimSpace(auth.Attributes["account_email"]); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func authProjectID(auth *coreauth.Auth) string {
|
||||
if auth == nil {
|
||||
return ""
|
||||
}
|
||||
if auth.Metadata != nil {
|
||||
if v, ok := auth.Metadata["project_id"].(string); ok {
|
||||
if projectID := strings.TrimSpace(v); projectID != "" {
|
||||
return projectID
|
||||
}
|
||||
}
|
||||
}
|
||||
if auth.Attributes != nil {
|
||||
if projectID := strings.TrimSpace(auth.Attributes["project_id"]); projectID != "" {
|
||||
return projectID
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func authAttribute(auth *coreauth.Auth, key string) string {
|
||||
if auth == nil || len(auth.Attributes) == 0 {
|
||||
return ""
|
||||
}
|
||||
return auth.Attributes[key]
|
||||
}
|
||||
|
||||
func isRuntimeOnlyAuth(auth *coreauth.Auth) bool {
|
||||
if auth == nil || len(auth.Attributes) == 0 {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(strings.TrimSpace(auth.Attributes["runtime_only"]), "true")
|
||||
}
|
||||
|
||||
func authWebsocketsValue(auth *coreauth.Auth) (bool, bool) {
|
||||
if auth == nil {
|
||||
return false, false
|
||||
}
|
||||
if auth.Attributes != nil {
|
||||
if raw := strings.TrimSpace(auth.Attributes["websockets"]); raw != "" {
|
||||
parsed, errParse := strconv.ParseBool(raw)
|
||||
if errParse == nil {
|
||||
return parsed, true
|
||||
}
|
||||
}
|
||||
}
|
||||
if auth.Metadata == nil {
|
||||
return false, false
|
||||
}
|
||||
return parseWebsocketsValue(auth.Metadata["websockets"])
|
||||
}
|
||||
|
||||
func parsePriorityValue(raw any) (int, bool) {
|
||||
switch v := raw.(type) {
|
||||
case int:
|
||||
return v, true
|
||||
case int32:
|
||||
return int(v), true
|
||||
case int64:
|
||||
return int(v), true
|
||||
case float64:
|
||||
return int(v), true
|
||||
case string:
|
||||
parsed, err := strconv.Atoi(strings.TrimSpace(v))
|
||||
if err == nil {
|
||||
return parsed, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func parseWebsocketsValue(raw any) (bool, bool) {
|
||||
switch v := raw.(type) {
|
||||
case bool:
|
||||
return v, true
|
||||
case string:
|
||||
parsed, errParse := strconv.ParseBool(strings.TrimSpace(v))
|
||||
if errParse == nil {
|
||||
return parsed, true
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func bytesTrimSpace(raw []byte) []byte {
|
||||
return []byte(strings.TrimSpace(string(raw)))
|
||||
}
|
||||
|
||||
func hostRecentRequests(auth *coreauth.Auth) []pluginapi.HostRecentRequestEntry {
|
||||
if auth == nil {
|
||||
return nil
|
||||
}
|
||||
snapshot := auth.RecentRequestsSnapshot(time.Now())
|
||||
if len(snapshot) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]pluginapi.HostRecentRequestEntry, 0, len(snapshot))
|
||||
for _, entry := range snapshot {
|
||||
out = append(out, pluginapi.HostRecentRequestEntry{
|
||||
Time: entry.Time,
|
||||
Success: entry.Success,
|
||||
Failed: entry.Failed,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
Loading…
Reference in a new issue