Files
Yeuoly 888ad788bc refactor: plugin lifecycle control panel (#499)
* refactor: introduce local plugin control panel and cleanup environment setup process

* fix: args

* refactor: new local runtime

* temp: stash work for refactor on RemotePluginServer

* refactor: unify local runtime lifetime and sperate init environment process

* chore: add missing files

* stash

* refactor: local plugin lifetime control

* refactor: complete installation process of control panel

* refactor: adapt service layer to new controlpanel

* refactor: pluginManager.Install

* fix: add routine wrap to InstallServerless, avoid blocking main thread

* feat: reinstall serverless runtime

* chore: add comments to Reinstall and update confusing naming

* refactor: unify install plugin service

* refactor: add labels to debugging runtime

* refactor: add getters to plugin manager

* refactor: split install service to decode/install_task/install service

* ???

* refactor: adapt controllers

* refactor: session write

* refactor: session runtime

* Refine install task orchestration (#501)

* refactor: installing task

* refactor cluster management, decouple lifetime management and cluster

* fix cli test command

* fix: cleanup TODO comments and implement GracefulStop for instance

* feat: add logger to control panel

* fix: multiple nil references

* refactor: better lifetime control

* refactor: better cycle interval

* fix(LocalPluginRuntime): prevent returning err when it's not  error

* fix: avoid adding empty PipExtraArgs

* fix: missing errors in Environment init

* fix: add truncateMessage to avoid db explosion

* cleanup: better lifecycle management

* fix: init status at the beginning of installation

* optimize: GracefulStop for pluginInstance

* refactor: tests

* refactor: centralize routine labels (#504)

* cleanup: RoutineKey

* fix: init routine pool

* fix: correctly handle cluster register error

* fix: memory leak

* fix: add \n to instance write

* fix(installer.go): set success to true after succeed for defer func

* refactor

* fix: missing cwd in testutils

* fix: scaleup default runtime nums to 1 when testing

* fix: localruntime appconfig in testing module

* Update internal/core/local_runtime/load_balancing.go

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* fix: more efficiency implement in installer_local.go

* fix: returns after failing in onDebuggingRuntimeDisconnected

* fix: returns after failing in onDebuggingRuntimeDisconnected

* fix: splits tests

* refactor: naming

* refactor: manifest.VersionX

* fix: adapt SetDefault to tests

* fix: enforce use constants in DBType

* fix: generate

* fix: linter

* cleanup tests

* refactor: change  package to

* cleanup: useless codes

* Update internal/cluster/plugin.go

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* cleanup

* refactor: decouple connection_key management from debugging_time

* refactor: confused naming

* feat: recycle resources to adapt to https://github.com/langgenius/dify-plugin-daemon/pull/500

* refactor: confusing redirecting

* fix: support get serverless runtime

* fix: race condition in Launching

* fix: avoid ManifestValidate in first step of debugging handshake

* fix: adding ReleaseAllLocks to finalizers

* wtf: what a beautiful code

* refactor: rename Stream.Async to Stream.Process

* fix: kill process if daed instance was detected

* fix: correctly handle failures

* fix: consistence of difference interfaces

* fix: add stacktrace to panic

* fix: only trigger once  event

* fix: ensure plugin runtime was shutdown

* feat: cleanup install tasks

* fix: add scale logs

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-18 17:28:02 +08:00

102 lines
3.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package server
import (
"errors"
"time"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache/helper"
"github.com/gin-gonic/gin"
"github.com/langgenius/dify-plugin-daemon/internal/db"
"github.com/langgenius/dify-plugin-daemon/internal/service"
"github.com/langgenius/dify-plugin-daemon/internal/types/app"
"github.com/langgenius/dify-plugin-daemon/internal/types/exception"
"github.com/langgenius/dify-plugin-daemon/internal/types/models"
"github.com/langgenius/dify-plugin-daemon/pkg/entities/endpoint_entities"
"github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/log"
)
// DifyPlugin supports register and use endpoint to improve the plugin's functionality
// you can use it to do some magics, looking forward to your imagination, Ciallo(∠·ω< )⌒
// - Yeuoly
// EndpointHandler is a function type that can be used to handle endpoint requests
type EndpointHandler func(ctx *gin.Context, hookId string, maxExecutionTime time.Duration, path string)
func (app *App) Endpoint(config *app.Config) func(c *gin.Context) {
return func(c *gin.Context) {
hookId := c.Param("hook_id")
path := c.Param("path")
// set X-Original-Host
if c.Request.Header.Get(endpoint_entities.HeaderXOriginalHost) == "" {
c.Request.Header.Set(endpoint_entities.HeaderXOriginalHost, c.Request.Host)
}
if app.endpointHandler != nil {
app.endpointHandler(c, hookId, time.Duration(config.PluginMaxExecutionTimeout)*time.Second, path)
} else {
app.EndpointHandler(c, hookId, time.Duration(config.PluginMaxExecutionTimeout)*time.Second, path)
}
}
}
func (app *App) EndpointHandler(ctx *gin.Context, hookId string, maxExecutionTime time.Duration, path string) {
endpointCacheKey := helper.EndpointCacheKey(hookId)
endpoint, err := cache.AutoGetWithGetter[models.Endpoint](
endpointCacheKey,
func() (*models.Endpoint, error) {
v, err := db.GetOne[models.Endpoint](
db.Equal("hook_id", hookId),
)
return &v, err
})
if errors.Is(err, db.ErrDatabaseNotFound) {
ctx.JSON(404, exception.BadRequestError(errors.New("endpoint not found")).ToResponse())
return
}
if err != nil {
log.Error("get endpoint error %v", err)
ctx.JSON(500, exception.InternalServerError(errors.New("internal server error")).ToResponse())
return
}
// get plugin installation
pluginInstallationCacheKey := helper.PluginInstallationCacheKey(endpoint.PluginID, endpoint.TenantID)
pluginInstallation, err := cache.AutoGetWithGetter[models.PluginInstallation](
pluginInstallationCacheKey,
func() (*models.PluginInstallation, error) {
v, err := db.GetOne[models.PluginInstallation](
db.Equal("plugin_id", endpoint.PluginID),
db.Equal("tenant_id", endpoint.TenantID),
)
return &v, err
},
)
if err != nil {
ctx.JSON(404, exception.BadRequestError(errors.New("plugin installation not found")).ToResponse())
return
}
pluginUniqueIdentifier, err := plugin_entities.NewPluginUniqueIdentifier(
pluginInstallation.PluginUniqueIdentifier,
)
if err != nil {
ctx.JSON(400, exception.UniqueIdentifierError(
errors.New("invalid plugin unique identifier"),
).ToResponse())
return
}
// check if plugin exists in current node
if needRedirecting, originalError := app.pluginManager.NeedRedirecting(pluginUniqueIdentifier); needRedirecting {
app.redirectPluginInvokeByPluginIdentifier(ctx, pluginUniqueIdentifier, originalError)
} else {
service.Endpoint(ctx, endpoint, pluginInstallation, maxExecutionTime, path)
}
}