mirror of
https://github.com/langgenius/dify-plugin-daemon.git
synced 2026-07-23 02:05:27 -04:00
4589841b0c
* feat(plugins): add FetchPluginReadme endpoint and update launch configurations * feat: add PluginReadme database model * feat: implement readme extracting and storage * feat: implement readme endpoint * feat: add plugin asset extraction endpoint with caching support * Implement trigger functionality and clean up dynamic select code - Added new trigger-related access types and actions in access.go. - Introduced new HTTP routes for trigger operations in http_server.gen.go. - Updated plugin declaration to include triggers in plugin_entities. - Removed unused dynamic select service and controller files. - Enhanced message handling in debugging_runtime to support trigger registration. This update enhances the plugin system by integrating trigger capabilities while cleaning up legacy code. * Refactor trigger-related types and enhance dynamic select functionality - Updated TriggerProviderIdentity and TriggerProviderConfiguration to improve structure and validation. - Renamed TriggerConfiguration to TriggerDeclaration for clarity. - Added CredentialType to RequestDynamicParameterSelect for better request handling. - Enhanced PluginDecoderHelper to read and unmarshal trigger files correctly. These changes streamline the trigger system and improve the overall request handling in the plugin architecture. * Add trigger functionality and enhance database integration - Introduced TriggerInstallation model for managing trigger installations in the database. - Updated autoMigrate function to include trigger installations in the migration process. - Added new HTTP routes for listing and retrieving triggers in the HTTP server. - Implemented ListTriggers and GetTrigger controller functions for handling trigger requests. - Enhanced plugin management functions to create, update, and delete trigger installations during plugin lifecycle events. These changes integrate trigger capabilities into the system, improving the overall plugin functionality and management. * feat: add remapping for trigger icons in MediaBucket - Enhanced the RemapAssets function to include remapping of trigger identity icons and dark icons. - Added error handling for remapping failures to ensure robust asset management. These changes improve the handling of trigger assets within the plugin system, ensuring icons are correctly remapped as needed. * feat: add Multiple field to TriggerParameter for enhanced configuration - Introduced a new Multiple field in the TriggerParameter struct to allow for multiple values in trigger configurations. - This addition improves the flexibility of trigger parameters within the plugin system. These changes enhance the capability of trigger parameters, enabling more complex configurations. * feat: add Multiple field to ProviderConfig for enhanced configuration - Introduced a new Multiple field in the ProviderConfig struct to allow for multiple values in provider configurations. - This addition improves the flexibility of provider options within the plugin system. These changes enhance the capability of provider configurations, enabling more complex setups. * fix(plugin): update validation error messages in ManifestValidate method - Enhanced error messages in the ManifestValidate function to include 'trigger' in the validation checks for plugin declarations. - Updated logic to ensure that all relevant fields are considered when validating the presence of mutually exclusive parameters. * feat(trigger): add CHECKBOX parameter type to plugin entities and refactor the trigger provider strcuture - Introduced a new CHECKBOX parameter type in constant.go for plugin entities. - Updated tool_declaration.go and trigger_declaration.go to include TOOL_PARAMETER_TYPE_CHECKBOX and TRIGGER_PARAMETER_TYPE_CHECKBOX respectively. - Enhanced validation logic to accommodate the new CHECKBOX type in parameter checks. * fix(trigger): update SubscriptionSchema validation in TriggerProviderDeclaration - Changed SubscriptionSchema validation from 'omitempty' to 'required' in TriggerProviderDeclaration to ensure it is always provided. - Updated SubscriptionConstructor field to be a pointer to allow for optional inclusion in the trigger provider configuration. * fix(trigger): rename ParametersSchema to Parameters in SubscriptionConstructor - Updated the SubscriptionConstructor struct to rename the ParametersSchema field to Parameters for consistency. - Adjusted related JSON and YAML marshaling logic to reflect the new field name, ensuring proper handling of trigger parameters. * refactor(trigger): enhance YAML unmarshalling for SubscriptionConstructor and SubscriptionSchema - Introduced a new helper function to convert YAML nodes to ProviderConfig lists, improving the handling of subscription_schema and credentials_schema. - Updated the UnmarshalYAML method to utilize the new function, simplifying the logic for parsing different YAML formats. - Ensured proper initialization of SubscriptionConstructor fields to prevent nil pointer dereferences. * fix(trigger): update SubscriptionConstructor validation in TriggerProviderDeclaration - Changed the validation for SubscriptionConstructor in TriggerProviderDeclaration from 'omitempty,dive' to 'omitempty' to simplify the validation logic. - Ensured that the SubscriptionConstructor field remains optional while maintaining its intended functionality. * refactor(trigger): rename Trigger to Event in plugin entities and related structures - Updated the naming conventions in trigger_declaration.go to replace 'Trigger' with 'Event' for better clarity and consistency. - Adjusted related types, validation functions, and unmarshalling logic to reflect the new 'Event' terminology. - Ensured that all references to triggers in the codebase are updated to events, including in the SubscriptionConstructor and response structures. * refactor(trigger): rename TriggerInvoke to TriggerInvokeEvent and update related structures - Renamed TriggerInvoke function and associated request/response types to TriggerInvokeEvent for improved clarity. - Updated routing and controller methods to reflect the new naming convention. - Ensured all references to the trigger invoke functionality are consistent with the new event terminology. * refactor(trigger): remove Subscription struct from trigger_declaration.go and update TriggerDispatchEventRequest - Removed the Subscription struct from trigger_declaration.go to streamline the codebase. - Added Credentials field to TriggerDispatchEventRequest for enhanced functionality and clarity. - Ensured that the changes maintain consistency with the existing naming conventions and structures. * fix(trigger): improve nil checks for SubscriptionConstructor in TriggerProviderDeclaration - Added nil checks for SubscriptionConstructor before accessing its fields to prevent potential nil pointer dereferences. - Ensured that Parameters and CredentialsSchema are initialized only if SubscriptionConstructor is not nil, enhancing code robustness. * fix(plugin): add recovery mechanism in OnTraffic to handle panics - Introduced a deferred function in OnTraffic to recover from panics, logging the error and stack trace for better debugging. - This enhancement improves the stability of the DifyServer by preventing crashes due to unexpected runtime errors. * feat(trigger): add Subscription field to TriggerInvokeEventRequest - Introduced a new Subscription field in the TriggerInvokeEventRequest struct to accommodate subscription data. - Ensured the field is marked as required, enhancing the request's functionality and validation requirements. * refactor(event): simplify EventDescription structure in EventDeclaration - Removed the EventDescription struct and replaced it with a direct I18nObject field in EventDeclaration. - This change streamlines the event configuration by reducing complexity while maintaining required validation for the description. * feat(trigger): add UserID field to TriggerDispatchEventResponse - Introduced a new UserID field in the TriggerDispatchEventResponse struct to include user identification in the response. - The field is marked as optional, enhancing the response's flexibility while maintaining existing functionality. * feat: add payload to TriggerDispatchEventResponse * fix * feat(trigger): update TriggerDispatchEventResponse structure * fix: avoid path collusion * fix: missing ) * fix: query param * fix: form param * fix: remove redundant dynamic parameter access type * fix: remove dynamic parameter access type from validation * Update internal/server/controllers/plugins.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: Stream <Stream_2@qq.com> Co-authored-by: Yeuoly <admin@srmxy.cn> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
206 lines
7.5 KiB
Go
206 lines
7.5 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_daemon/backwards_invocation/transaction"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/server/controllers"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/service"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/types/app"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/utils/log"
|
|
|
|
sentrygin "github.com/getsentry/sentry-go/gin"
|
|
)
|
|
|
|
// server starts a http server and returns a function to stop it
|
|
func (app *App) server(config *app.Config) func() {
|
|
engine := gin.New()
|
|
if *config.HealthApiLogEnabled {
|
|
engine.Use(gin.Logger())
|
|
} else {
|
|
engine.Use(gin.LoggerWithConfig(gin.LoggerConfig{
|
|
SkipPaths: []string{"/health/check"},
|
|
}))
|
|
}
|
|
engine.Use(gin.Recovery())
|
|
engine.Use(controllers.CollectActiveRequests())
|
|
engine.GET("/health/check", controllers.HealthCheck(config))
|
|
|
|
endpointGroup := engine.Group("/e")
|
|
serverlessTransactionGroup := engine.Group("/backwards-invocation")
|
|
pluginGroup := engine.Group("/plugin/:tenant_id")
|
|
pprofGroup := engine.Group("/debug/pprof")
|
|
|
|
if config.AdminApiEnabled {
|
|
if len(config.AdminApiKey) < 10 {
|
|
log.Panic("length of admin api key must be greater than 10")
|
|
}
|
|
|
|
adminGroup := engine.Group("/admin")
|
|
adminGroup.Use(app.AdminAPIKey(config.AdminApiKey))
|
|
|
|
app.adminGroup(adminGroup, config)
|
|
}
|
|
|
|
if config.SentryEnabled {
|
|
// setup sentry for all groups
|
|
sentryGroup := []*gin.RouterGroup{
|
|
endpointGroup,
|
|
serverlessTransactionGroup,
|
|
pluginGroup,
|
|
}
|
|
for _, group := range sentryGroup {
|
|
group.Use(sentrygin.New(sentrygin.Options{
|
|
Repanic: true,
|
|
}))
|
|
}
|
|
}
|
|
|
|
app.endpointGroup(endpointGroup, config)
|
|
app.serverlessTransactionGroup(serverlessTransactionGroup, config)
|
|
app.pluginGroup(pluginGroup, config)
|
|
app.pprofGroup(pprofGroup, config)
|
|
|
|
srv := &http.Server{
|
|
Addr: fmt.Sprintf(":%d", config.ServerPort),
|
|
Handler: engine,
|
|
}
|
|
|
|
go func() {
|
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Panic("listen: %s\n", err)
|
|
}
|
|
}()
|
|
|
|
return func() {
|
|
if err := srv.Shutdown(context.Background()); err != nil {
|
|
log.Panic("Server Shutdown: %s\n", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (app *App) pluginGroup(group *gin.RouterGroup, config *app.Config) {
|
|
group.Use(CheckingKey(config.ServerKey))
|
|
|
|
app.remoteDebuggingGroup(group.Group("/debugging"), config)
|
|
app.pluginDispatchGroup(group.Group("/dispatch"), config)
|
|
app.pluginManagementGroup(group.Group("/management"), config)
|
|
app.endpointManagementGroup(group.Group("/endpoint"))
|
|
app.pluginAssetGroup(group.Group("/asset"))
|
|
app.pluginAssetExtractGroup(group.Group("/extract-asset"))
|
|
}
|
|
|
|
func (app *App) pluginDispatchGroup(group *gin.RouterGroup, config *app.Config) {
|
|
group.Use(controllers.CollectActiveDispatchRequests())
|
|
group.Use(app.FetchPluginInstallation())
|
|
group.Use(app.RedirectPluginInvoke())
|
|
group.Use(app.InitClusterID())
|
|
|
|
group.POST("/agent_strategy/invoke", controllers.InvokeAgentStrategy(config))
|
|
|
|
app.setupGeneratedRoutes(group, config)
|
|
}
|
|
|
|
func (app *App) remoteDebuggingGroup(group *gin.RouterGroup, config *app.Config) {
|
|
if config.PluginRemoteInstallingEnabled != nil && *config.PluginRemoteInstallingEnabled {
|
|
group.POST("/key", CheckingKey(config.ServerKey), controllers.GetRemoteDebuggingKey)
|
|
}
|
|
}
|
|
|
|
func (app *App) endpointGroup(group *gin.RouterGroup, config *app.Config) {
|
|
if config.PluginEndpointEnabled != nil && *config.PluginEndpointEnabled {
|
|
group.HEAD("/:hook_id/*path", app.Endpoint(config))
|
|
group.POST("/:hook_id/*path", app.Endpoint(config))
|
|
group.GET("/:hook_id/*path", app.Endpoint(config))
|
|
group.PUT("/:hook_id/*path", app.Endpoint(config))
|
|
group.DELETE("/:hook_id/*path", app.Endpoint(config))
|
|
group.OPTIONS("/:hook_id/*path", app.Endpoint(config))
|
|
}
|
|
}
|
|
|
|
func (appRef *App) serverlessTransactionGroup(group *gin.RouterGroup, config *app.Config) {
|
|
if config.Platform == app.PLATFORM_SERVERLESS {
|
|
appRef.serverlessTransactionHandler = transaction.NewServerlessTransactionHandler(
|
|
time.Duration(config.MaxServerlessTransactionTimeout) * time.Second,
|
|
)
|
|
group.POST(
|
|
"/transaction",
|
|
service.HandleServerlessPluginTransaction(appRef.serverlessTransactionHandler),
|
|
)
|
|
}
|
|
}
|
|
|
|
func (app *App) endpointManagementGroup(group *gin.RouterGroup) {
|
|
group.POST("/setup", controllers.SetupEndpoint)
|
|
group.POST("/remove", controllers.RemoveEndpoint)
|
|
group.POST("/update", controllers.UpdateEndpoint)
|
|
group.GET("/list", controllers.ListEndpoints)
|
|
group.GET("/list/plugin", controllers.ListPluginEndpoints)
|
|
group.POST("/enable", controllers.EnableEndpoint)
|
|
group.POST("/disable", controllers.DisableEndpoint)
|
|
}
|
|
|
|
func (app *App) pluginManagementGroup(group *gin.RouterGroup, config *app.Config) {
|
|
group.POST("/install/upload/package", controllers.UploadPlugin(config))
|
|
group.POST("/install/upload/bundle", controllers.UploadBundle(config))
|
|
group.POST("/install/identifiers", controllers.InstallPluginFromIdentifiers(config))
|
|
group.POST("/install/upgrade", controllers.UpgradePlugin(config))
|
|
group.GET("/install/tasks/:id", controllers.FetchPluginInstallationTask)
|
|
group.POST("/install/tasks/delete_all", controllers.DeleteAllPluginInstallationTasks)
|
|
group.POST("/install/tasks/:id/delete", controllers.DeletePluginInstallationTask)
|
|
group.POST("/install/tasks/:id/delete/*identifier", controllers.DeletePluginInstallationItemFromTask)
|
|
group.GET("/install/tasks", controllers.FetchPluginInstallationTasks)
|
|
group.GET("/decode/from_identifier", controllers.DecodePluginFromIdentifier(config))
|
|
group.GET("/fetch/manifest", controllers.FetchPluginManifest)
|
|
group.GET("/fetch/identifier", controllers.FetchPluginFromIdentifier)
|
|
group.GET("/fetch/readme", controllers.FetchPluginReadme)
|
|
group.POST("/uninstall", controllers.UninstallPlugin)
|
|
group.GET("/list", controllers.ListPlugins)
|
|
group.POST("/installation/fetch/batch", controllers.BatchFetchPluginInstallationByIDs)
|
|
group.POST("/installation/missing", controllers.FetchMissingPluginInstallations)
|
|
group.GET("/models", controllers.ListModels)
|
|
group.GET("/tools", controllers.ListTools)
|
|
group.GET("/tool", controllers.GetTool)
|
|
group.GET("/triggers", controllers.ListTriggers)
|
|
group.GET("/trigger", controllers.GetTrigger)
|
|
group.POST("/tools/check_existence", controllers.CheckToolExistence)
|
|
group.GET("/agent_strategies", controllers.ListAgentStrategies)
|
|
group.GET("/agent_strategy", controllers.GetAgentStrategy)
|
|
group.GET("/datasources", controllers.ListDatasources)
|
|
group.GET("/datasource", controllers.GetDatasource)
|
|
}
|
|
|
|
func (app *App) adminGroup(group *gin.RouterGroup, config *app.Config) {
|
|
group.POST("/plugin/serverless/reinstall", controllers.ReinstallPluginFromIdentifier(config))
|
|
}
|
|
|
|
func (app *App) pluginAssetGroup(group *gin.RouterGroup) {
|
|
group.GET("/:id", controllers.GetAsset)
|
|
}
|
|
|
|
func (app *App) pluginAssetExtractGroup(group *gin.RouterGroup) {
|
|
group.GET("/", controllers.ExtractPluginAsset)
|
|
}
|
|
|
|
func (app *App) pprofGroup(group *gin.RouterGroup, config *app.Config) {
|
|
if config.PPROFEnabled {
|
|
group.Use(CheckingKey(config.ServerKey))
|
|
|
|
group.GET("/", controllers.PprofIndex)
|
|
group.GET("/cmdline", controllers.PprofCmdline)
|
|
group.GET("/profile", controllers.PprofProfile)
|
|
group.GET("/symbol", controllers.PprofSymbol)
|
|
group.GET("/trace", controllers.PprofTrace)
|
|
group.GET("/goroutine", controllers.PprofGoroutine)
|
|
group.GET("/heap", controllers.PprofHeap)
|
|
group.GET("/allocs", controllers.PprofAllocs)
|
|
group.GET("/block", controllers.PprofBlock)
|
|
group.GET("/mutex", controllers.PprofMutex)
|
|
group.GET("/threadcreate", controllers.PprofThreadcreate)
|
|
}
|
|
}
|