feat: publish sanitized custom.97 source

This commit is contained in:
cneicy committed 2026-10-27 20:43:39 +08:00
1 parent 8e6ed0317b
commit 86c0276156
1022 files changed
+69903 -19207

No files matched your search

+41 -17
View File
@@ -15,7 +15,7 @@ import (
"time"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/i18n"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/logger"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/pkg/jsplugin"
@@ -25,12 +25,12 @@ import (
"gorm.io/gorm"
)
const maxTaskPluginSourceBytes = 1024 * 1024
const maxTaskPluginSourceBytes = 8 << 20
func taskPluginCompileError(c *gin.Context, err error) {
var unknownField *jsplugin.UnknownMetaFieldError
if errors.As(err, &unknownField) {
common.ApiErrorI18n(c, i18n.MsgTaskPluginUnknownMetaField, map[string]any{"Field": unknownField.Field})
common.ApiErrorT(c, "Plugin metadata contains an unknown field \"{{field}}\". If this plugin was downloaded from the official marketplace, it may require a newer version of new-api. Try updating new-api and installing the plugin again.", map[string]any{"field": unknownField.Field})
return
}
common.ApiError(c, err)
@@ -54,7 +54,7 @@ func UploadTaskPlugin(c *gin.Context) {
return
}
if len(request.Source) > maxTaskPluginSourceBytes {
common.ApiErrorMsg(c, "plugin source exceeds 1 MiB")
common.ApiErrorMsg(c, "plugin source exceeds 8 MiB")
return
}
if expected := strings.TrimSpace(request.SourceSha256); expected != "" {
@@ -93,8 +93,8 @@ func UploadTaskPlugin(c *gin.Context) {
}
plugin := model.TaskPlugin{
Key: loaded.Meta.Key, APIVersion: loaded.Meta.APIVersion, Version: loaded.Meta.Version,
Source: request.Source, SourceHash: fmt.Sprintf("%x", sha256.Sum256([]byte(request.Source))),
Icon: icon, Enabled: enabled, Remark: request.Remark,
Source: model.LongText(request.Source), SourceHash: fmt.Sprintf("%x", sha256.Sum256([]byte(request.Source))),
Icon: model.LongText(icon), Enabled: enabled, Remark: request.Remark,
}
if err = model.SaveTaskPlugin(&plugin); err != nil {
common.ApiError(c, err)
@@ -104,7 +104,7 @@ func UploadTaskPlugin(c *gin.Context) {
common.ApiError(c, err)
return
}
common.ApiSuccess(c, taskPluginDetail{Plugin: &plugin, Meta: loaded.Meta, Source: plugin.Source, Layer: "override", HasIcon: plugin.HasIcon()})
common.ApiSuccess(c, taskPluginDetail{Plugin: &plugin, Meta: loaded.Meta, Source: string(plugin.Source), Layer: "override", HasIcon: plugin.HasIcon()})
}
func GetTaskPluginVersions(c *gin.Context) {
@@ -194,7 +194,7 @@ func ListTaskPlugins(c *gin.Context) {
item.FactoryMeta = &factoryCopy
}
item.Meta = jsplugin.Meta{Key: row.Key, Version: row.Version, APIVersion: row.APIVersion}
if compiled, compileErr := jsplugin.NewRegistry().Register(row.Source, jsplugin.Options{Key: row.Key, Version: row.Version}); compileErr == nil {
if compiled, compileErr := jsplugin.NewRegistry().Register(string(row.Source), jsplugin.Options{Key: row.Key, Version: row.Version}); compileErr == nil {
item.Meta = compiled.Meta
}
item.Enabled = row.Enabled
@@ -315,7 +315,7 @@ func GetTaskPluginIcon(c *gin.Context) {
icon := ""
plugin, err := model.GetTaskPluginVersion(key, c.Query("version"))
if err == nil {
icon = plugin.Icon
icon = string(plugin.Icon)
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
common.ApiError(c, err)
return
@@ -343,12 +343,12 @@ func GetTaskPlugin(c *gin.Context) {
version := c.Query("version")
plugin, err := model.GetTaskPluginVersion(key, version)
if err == nil {
loaded, compileErr := jsplugin.NewRegistry().Register(plugin.Source, jsplugin.Options{Key: plugin.Key, Version: plugin.Version})
loaded, compileErr := jsplugin.NewRegistry().Register(string(plugin.Source), jsplugin.Options{Key: plugin.Key, Version: plugin.Version})
if compileErr != nil {
taskPluginCompileError(c, compileErr)
return
}
common.ApiSuccess(c, taskPluginDetail{Plugin: plugin, Meta: loaded.Meta, Source: plugin.Source, Layer: "override", HasIcon: plugin.HasIcon()})
common.ApiSuccess(c, taskPluginDetail{Plugin: plugin, Meta: loaded.Meta, Source: string(plugin.Source), Layer: "override", HasIcon: plugin.HasIcon()})
return
}
if !errors.Is(err, gorm.ErrRecordNotFound) || version != "" {
@@ -384,7 +384,7 @@ func DryRunTaskPlugin(c *gin.Context) {
detailSource := ""
plugin, err := model.GetTaskPluginVersion(c.Param("key"), "")
if err == nil {
detailSource = plugin.Source
detailSource = string(plugin.Source)
} else if errors.Is(err, gorm.ErrRecordNotFound) {
detailSource, err = plugins.Source(c.Param("key"))
}
@@ -482,7 +482,7 @@ func ActivateTaskPlugin(c *gin.Context) {
common.ApiErrorMsg(c, "plugin version not found")
return
}
if _, err = jsplugin.NewRegistry().Register(target.Source, jsplugin.Options{Key: target.Key, Version: target.Version}); err != nil {
if _, err = jsplugin.NewRegistry().Register(string(target.Source), jsplugin.Options{Key: target.Key, Version: target.Version}); err != nil {
taskPluginCompileError(c, err)
return
}
@@ -509,6 +509,7 @@ func SetTaskPluginStatus(c *gin.Context) {
}
key := c.Param("key")
disabledChannels := 0
unboundChannels := 0
if !*request.Enabled {
channels, inFlight, usageErr := model.GetTaskPluginUsage(key)
if usageErr != nil {
@@ -523,10 +524,26 @@ func SetTaskPluginStatus(c *gin.Context) {
}
if cascade {
for _, channel := range channels {
if channel.Type == constant.ChannelTypeNewAPI {
// A gateway channel also carries ordinary traffic, so the
// cascade only drops this plugin from its bindings.
unbound, unbindErr := model.UnbindTaskPlugin(channel.Id, key)
if unbindErr != nil {
common.ApiError(c, unbindErr)
return
}
if unbound {
unboundChannels++
}
continue
}
if model.UpdateChannelStatus(channel.Id, "", common.ChannelStatusManuallyDisabled, "task plugin disabled") {
disabledChannels++
}
}
if unboundChannels > 0 {
model.InitChannelCache()
}
}
}
_, lookupErr := model.GetTaskPluginVersion(key, "")
@@ -567,7 +584,7 @@ func SetTaskPluginStatus(c *gin.Context) {
return
}
if !hasActiveOverride {
common.ApiSuccess(c, gin.H{"plugin_enabled": *request.Enabled, "disabled_channels": disabledChannels})
common.ApiSuccess(c, gin.H{"plugin_enabled": *request.Enabled, "disabled_channels": disabledChannels, "unbound_channels": unboundChannels})
return
}
}
@@ -579,7 +596,7 @@ func SetTaskPluginStatus(c *gin.Context) {
common.ApiError(c, err)
return
}
common.ApiSuccess(c, gin.H{"plugin_enabled": *request.Enabled, "disabled_channels": disabledChannels})
common.ApiSuccess(c, gin.H{"plugin_enabled": *request.Enabled, "disabled_channels": disabledChannels, "unbound_channels": unboundChannels})
}
func taskPluginHasFactory(key string) bool {
@@ -669,6 +686,7 @@ func GetTaskPluginOptions(c *gin.Context) {
"website": meta.Website,
"models": meta.Models,
"channelTypes": meta.ChannelTypes,
"upstreams": meta.Upstreams,
"usageSchema": meta.UsageSchema,
"usageProfiles": meta.UsageProfiles,
})
@@ -751,7 +769,13 @@ func syncTaskPluginsOnceContext(ctx context.Context) error {
plugin.Key,
plugin.Version,
)
compiled, compileErr := jsplugin.CompilePlugin(plugin.Source, jsplugin.Options{Key: plugin.Key, Version: plugin.Version})
var compiled *jsplugin.LoadedPlugin
source, compileErr := model.GetTaskPluginSource(plugin.Id)
if compileErr != nil {
compileErr = fmt.Errorf("load source: %w", compileErr)
} else {
compiled, compileErr = jsplugin.CompilePlugin(string(source), jsplugin.Options{Key: plugin.Key, Version: plugin.Version})
}
if compileErr != nil {
retainedIncumbent := false
if current := currentOverrides[plugin.Key]; current != nil {
@@ -762,7 +786,7 @@ func syncTaskPluginsOnceContext(ctx context.Context) error {
}
}
taskPluginSyncState.errors[plugin.Key] = compileErr.Error()
common.SysError(fmt.Sprintf("compile task plugin %s@%s: %v", plugin.Key, plugin.Version, compileErr))
common.SysError(common.LogText("compile task plugin %s@%s: %v", plugin.Key, plugin.Version, compileErr))
logger.LogDebug(
ctx,
"task_plugin subsystem=sync event=plugin plugin=%q version=%q action=compile_failed retained_incumbent=%t",