node build fixed
This commit is contained in:
399
seanime-2.9.10/internal/extension/plugin.go
Normal file
399
seanime-2.9.10/internal/extension/plugin.go
Normal file
@@ -0,0 +1,399 @@
|
||||
package extension
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PluginManifestVersion = "1"
|
||||
)
|
||||
|
||||
var (
|
||||
PluginPermissionStorage PluginPermissionScope = "storage" // Allows the plugin to store its own data
|
||||
PluginPermissionDatabase PluginPermissionScope = "database" // Allows the plugin to read non-auth data from the database and write to it
|
||||
PluginPermissionPlayback PluginPermissionScope = "playback" // Allows the plugin to use the playback manager
|
||||
PluginPermissionAnilist PluginPermissionScope = "anilist" // Allows the plugin to use the Anilist client
|
||||
PluginPermissionAnilistToken PluginPermissionScope = "anilist-token" // Allows the plugin to see and use the Anilist token
|
||||
PluginPermissionSystem PluginPermissionScope = "system" // Allows the plugin to use the OS/Filesystem/Filepath functions. SystemPermissions must be granted additionally.
|
||||
PluginPermissionCron PluginPermissionScope = "cron" // Allows the plugin to use the cron manager
|
||||
PluginPermissionNotification PluginPermissionScope = "notification" // Allows the plugin to use the notification manager
|
||||
PluginPermissionDiscord PluginPermissionScope = "discord" // Allows the plugin to use the discord rpc
|
||||
PluginPermissionTorrentClient PluginPermissionScope = "torrent-client" // Allows the plugin to use the torrent client
|
||||
)
|
||||
|
||||
type PluginManifest struct {
|
||||
Version string `json:"version"`
|
||||
// Permissions is a list of permissions that the plugin is asking for.
|
||||
// The user must acknowledge these permissions before the plugin can be loaded.
|
||||
Permissions PluginPermissions `json:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
type PluginPermissions struct {
|
||||
Scopes []PluginPermissionScope `json:"scopes,omitempty"`
|
||||
Allow PluginAllowlist `json:"allow,omitempty"`
|
||||
}
|
||||
|
||||
// PluginAllowlist is a list of system permissions that the plugin is asking for.
|
||||
//
|
||||
// The user must acknowledge these permissions before the plugin can be loaded.
|
||||
type PluginAllowlist struct {
|
||||
// ReadPaths is a list of paths that the plugin is allowed to read from.
|
||||
ReadPaths []string `json:"readPaths,omitempty"`
|
||||
// WritePaths is a list of paths that the plugin is allowed to write to.
|
||||
WritePaths []string `json:"writePaths,omitempty"`
|
||||
// CommandScopes defines the commands that the plugin is allowed to execute.
|
||||
// Each command scope has a unique identifier and configuration.
|
||||
CommandScopes []CommandScope `json:"commandScopes,omitempty"`
|
||||
}
|
||||
|
||||
// CommandScope defines a specific command or set of commands that can be executed
|
||||
// with specific arguments and validation rules.
|
||||
type CommandScope struct {
|
||||
// Description explains why this command scope is needed
|
||||
Description string `json:"description,omitempty"`
|
||||
// Command is the executable program
|
||||
Command string `json:"command"`
|
||||
// Args defines the allowed arguments for this command
|
||||
// If nil or empty, no arguments are allowed
|
||||
// If contains "$ARGS", any arguments are allowed at that position
|
||||
Args []CommandArg `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
// CommandArg represents an argument for a command
|
||||
type CommandArg struct {
|
||||
// Value is the fixed argument value
|
||||
// If empty, Validator must be set
|
||||
Value string `json:"value,omitempty"`
|
||||
// Validator is a Perl compatible regex pattern to validate dynamic argument values
|
||||
// Special values:
|
||||
// - "$ARGS" allows any arguments at this position
|
||||
// - "$PATH" allows any valid file path
|
||||
Validator string `json:"validator,omitempty"`
|
||||
}
|
||||
|
||||
// ReadAllowCommands returns a human-readable representation of the commands
|
||||
// that the plugin is allowed to execute.
|
||||
func (p *PluginAllowlist) ReadAllowCommands() []string {
|
||||
if p == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
result := make([]string, 0)
|
||||
|
||||
// Add commands from CommandScopes
|
||||
if len(p.CommandScopes) > 0 {
|
||||
for _, scope := range p.CommandScopes {
|
||||
cmd := scope.Command
|
||||
|
||||
// Build argument string
|
||||
args := ""
|
||||
for i, arg := range scope.Args {
|
||||
if i > 0 {
|
||||
args += " "
|
||||
}
|
||||
|
||||
if arg.Value != "" {
|
||||
args += arg.Value
|
||||
} else if arg.Validator == "$ARGS" {
|
||||
args += "[any arguments]"
|
||||
} else if arg.Validator == "$PATH" {
|
||||
args += "[any path]"
|
||||
} else if arg.Validator != "" {
|
||||
args += "[matching: " + arg.Validator + "]"
|
||||
}
|
||||
}
|
||||
|
||||
if args != "" {
|
||||
cmd += " " + args
|
||||
}
|
||||
|
||||
// Add description if available
|
||||
if scope.Description != "" {
|
||||
cmd += " - " + scope.Description
|
||||
}
|
||||
|
||||
result = append(result, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *PluginPermissions) GetHash() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(p.Scopes) == 0 &&
|
||||
len(p.Allow.ReadPaths) == 0 &&
|
||||
len(p.Allow.WritePaths) == 0 &&
|
||||
len(p.Allow.CommandScopes) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
|
||||
// Hash scopes
|
||||
for _, scope := range p.Scopes {
|
||||
h.Write([]byte(scope))
|
||||
}
|
||||
|
||||
// Hash allowlist read paths
|
||||
for _, path := range p.Allow.ReadPaths {
|
||||
h.Write([]byte("read:" + path))
|
||||
}
|
||||
|
||||
// Hash allowlist write paths
|
||||
for _, path := range p.Allow.WritePaths {
|
||||
h.Write([]byte("write:" + path))
|
||||
}
|
||||
|
||||
// Hash command scopes
|
||||
for _, cmd := range p.Allow.CommandScopes {
|
||||
h.Write([]byte("cmd:" + cmd.Command + ":" + cmd.Description))
|
||||
for _, arg := range cmd.Args {
|
||||
h.Write([]byte("arg:" + arg.Value + ":" + arg.Validator))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
func (p *PluginPermissions) GetDescription() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Check if any permissions exist
|
||||
if len(p.Scopes) == 0 &&
|
||||
len(p.Allow.ReadPaths) == 0 &&
|
||||
len(p.Allow.WritePaths) == 0 &&
|
||||
len(p.Allow.CommandScopes) == 0 {
|
||||
return "No permissions requested."
|
||||
}
|
||||
|
||||
var desc strings.Builder
|
||||
|
||||
// Add scopes section if any exist
|
||||
if len(p.Scopes) > 0 {
|
||||
desc.WriteString("Application:\n")
|
||||
for _, scope := range p.Scopes {
|
||||
desc.WriteString("• ")
|
||||
switch scope {
|
||||
case PluginPermissionStorage:
|
||||
desc.WriteString("Storage: Store plugin data locally\n")
|
||||
case PluginPermissionDatabase:
|
||||
desc.WriteString("Database: Read and write non-auth data\n")
|
||||
case PluginPermissionPlayback:
|
||||
desc.WriteString("Playback: Control media playback and media players\n")
|
||||
case PluginPermissionAnilist:
|
||||
desc.WriteString("AniList: View and edit your AniList lists\n")
|
||||
case PluginPermissionAnilistToken:
|
||||
desc.WriteString("AniList Token: View and use your AniList token\n")
|
||||
case PluginPermissionSystem:
|
||||
desc.WriteString("System: Access OS functions (accessing files, running commands, etc.)\n")
|
||||
case PluginPermissionCron:
|
||||
desc.WriteString("Cron: Schedule automated tasks\n")
|
||||
case PluginPermissionNotification:
|
||||
desc.WriteString("Notification: Send system notifications\n")
|
||||
case PluginPermissionDiscord:
|
||||
desc.WriteString("Discord: Set Discord Rich Presence\n")
|
||||
case PluginPermissionTorrentClient:
|
||||
desc.WriteString("Torrent Client: Control torrent clients\n")
|
||||
default:
|
||||
desc.WriteString(string(scope) + "\n")
|
||||
}
|
||||
}
|
||||
desc.WriteString("\n")
|
||||
}
|
||||
|
||||
// Add file permissions if any exist
|
||||
hasFilePaths := len(p.Allow.ReadPaths) > 0 || len(p.Allow.WritePaths) > 0
|
||||
if hasFilePaths {
|
||||
desc.WriteString("File System:\n")
|
||||
|
||||
if len(p.Allow.ReadPaths) > 0 {
|
||||
desc.WriteString("• Read from:\n")
|
||||
for _, path := range p.Allow.ReadPaths {
|
||||
desc.WriteString("\t - " + explainPath(path) + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.Allow.WritePaths) > 0 {
|
||||
desc.WriteString("• Write to:\n")
|
||||
for _, path := range p.Allow.WritePaths {
|
||||
desc.WriteString("\t - " + explainPath(path) + "\n")
|
||||
}
|
||||
}
|
||||
desc.WriteString("\n")
|
||||
}
|
||||
|
||||
// Add command permissions if any exist
|
||||
if len(p.Allow.CommandScopes) > 0 {
|
||||
desc.WriteString("Commands:\n")
|
||||
for _, cmd := range p.Allow.CommandScopes {
|
||||
cmdDesc := "• " + cmd.Command
|
||||
|
||||
// Format arguments
|
||||
if len(cmd.Args) > 0 {
|
||||
argsDesc := ""
|
||||
for _, arg := range cmd.Args {
|
||||
if arg.Value != "" {
|
||||
argsDesc += " " + arg.Value
|
||||
} else if arg.Validator == "$ARGS" {
|
||||
argsDesc += " [any arguments]"
|
||||
} else if arg.Validator == "$PATH" {
|
||||
argsDesc += " [any file path]"
|
||||
} else if arg.Validator != "" {
|
||||
argsDesc += " [pattern: " + arg.Validator + "]"
|
||||
}
|
||||
}
|
||||
cmdDesc += argsDesc
|
||||
}
|
||||
|
||||
// Add command description if available
|
||||
if cmd.Description != "" {
|
||||
cmdDesc += "\n\t Purpose: " + cmd.Description
|
||||
}
|
||||
|
||||
desc.WriteString(cmdDesc + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(desc.String())
|
||||
}
|
||||
|
||||
// explainPath adds human-readable descriptions to paths containing environment variables
|
||||
func explainPath(path string) string {
|
||||
environmentVars := map[string]string{
|
||||
"$SEANIME_ANIME_LIBRARY": "Your anime library directories",
|
||||
"$HOME": "Your system's Home directory",
|
||||
"$CACHE": "Your system's Cache directory",
|
||||
"$TEMP": "Your system's Temporary directory",
|
||||
"$CONFIG": "Your system's Config directory",
|
||||
"$DOWNLOAD": "Your system's Downloads directory",
|
||||
"$DESKTOP": "Your system's Desktop directory",
|
||||
"$DOCUMENT": "Your system's Documents directory",
|
||||
}
|
||||
|
||||
result := path
|
||||
|
||||
// Check if we need to add an explanation
|
||||
needsExplanation := false
|
||||
explanation := ""
|
||||
|
||||
for envVar, description := range environmentVars {
|
||||
if strings.Contains(path, envVar) {
|
||||
if explanation != "" {
|
||||
explanation += ", "
|
||||
}
|
||||
explanation += fmt.Sprintf("%s = %s", envVar, description)
|
||||
needsExplanation = true
|
||||
}
|
||||
}
|
||||
|
||||
if needsExplanation {
|
||||
result += " (" + explanation + ")"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type PluginExtension interface {
|
||||
BaseExtension
|
||||
GetPermissionHash() string
|
||||
}
|
||||
|
||||
type PluginExtensionImpl struct {
|
||||
ext *Extension
|
||||
}
|
||||
|
||||
func NewPluginExtension(ext *Extension) PluginExtension {
|
||||
return &PluginExtensionImpl{
|
||||
ext: ext,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetPermissionHash() string {
|
||||
if m.ext.Plugin == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return m.ext.Plugin.Permissions.GetHash()
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetExtension() *Extension {
|
||||
return m.ext
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetType() Type {
|
||||
return m.ext.Type
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetID() string {
|
||||
return m.ext.ID
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetName() string {
|
||||
return m.ext.Name
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetVersion() string {
|
||||
return m.ext.Version
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetManifestURI() string {
|
||||
return m.ext.ManifestURI
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetLanguage() Language {
|
||||
return m.ext.Language
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetLang() string {
|
||||
return GetExtensionLang(m.ext.Lang)
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetDescription() string {
|
||||
return m.ext.Description
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetAuthor() string {
|
||||
return m.ext.Author
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetPayload() string {
|
||||
return m.ext.Payload
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetWebsite() string {
|
||||
return m.ext.Website
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetIcon() string {
|
||||
return m.ext.Icon
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetPermissions() []string {
|
||||
return m.ext.Permissions
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetUserConfig() *UserConfig {
|
||||
return m.ext.UserConfig
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetSavedUserConfig() *SavedUserConfig {
|
||||
return m.ext.SavedUserConfig
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetPayloadURI() string {
|
||||
return m.ext.PayloadURI
|
||||
}
|
||||
|
||||
func (m *PluginExtensionImpl) GetIsDevelopment() bool {
|
||||
return m.ext.IsDevelopment
|
||||
}
|
||||
Reference in New Issue
Block a user