@@ -26,6 +26,7 @@ import (
2626 "github.com/codex2api/database"
2727 "github.com/codex2api/proxy"
2828 "github.com/codex2api/security"
29+ "github.com/codex2api/security/promptfilter"
2930 "github.com/gin-gonic/gin"
3031 "github.com/tidwall/gjson"
3132)
@@ -133,6 +134,10 @@ func (h *Handler) RegisterRoutes(r *gin.Engine) {
133134 api .GET ("/ops/overview" , h .GetOpsOverview )
134135 api .GET ("/settings" , h .GetSettings )
135136 api .PUT ("/settings" , h .UpdateSettings )
137+ api .GET ("/prompt-filter/logs" , h .ListPromptFilterLogs )
138+ api .DELETE ("/prompt-filter/logs" , h .ClearPromptFilterLogs )
139+ api .POST ("/prompt-filter/test" , h .TestPromptFilter )
140+ api .GET ("/prompt-filter/rules" , h .GetPromptFilterRules )
136141 api .GET ("/models" , h .ListModels )
137142 api .POST ("/models/sync" , h .SyncModels )
138143 api .GET ("/image-prompts" , h .ListImagePromptTemplates )
@@ -297,10 +302,10 @@ type accountResponse struct {
297302 Locked bool `json:"locked"`
298303 AllowedAPIKeyIDs []int64 `json:"allowed_api_key_ids"`
299304 // 图片配额信息
300- ImageQuotaRemaining * int `json:"image_quota_remaining,omitempty"`
301- ImageQuotaTotal * int `json:"image_quota_total,omitempty"`
302- TodayUsedCount * int `json:"today_used_count,omitempty"`
303- ImageQuotaResetAt string `json:"image_quota_reset_at,omitempty"`
305+ ImageQuotaRemaining * int `json:"image_quota_remaining,omitempty"`
306+ ImageQuotaTotal * int `json:"image_quota_total,omitempty"`
307+ TodayUsedCount * int `json:"today_used_count,omitempty"`
308+ ImageQuotaResetAt string `json:"image_quota_reset_at,omitempty"`
304309}
305310
306311type accountUsageWindow struct {
@@ -2090,6 +2095,15 @@ type settingsResponse struct {
20902095 ModelMapping string `json:"model_mapping"`
20912096 ResinURL string `json:"resin_url"`
20922097 ResinPlatformName string `json:"resin_platform_name"`
2098+ PromptFilterEnabled bool `json:"prompt_filter_enabled"`
2099+ PromptFilterMode string `json:"prompt_filter_mode"`
2100+ PromptFilterThreshold int `json:"prompt_filter_threshold"`
2101+ PromptFilterStrictThreshold int `json:"prompt_filter_strict_threshold"`
2102+ PromptFilterLogMatches bool `json:"prompt_filter_log_matches"`
2103+ PromptFilterMaxTextLength int `json:"prompt_filter_max_text_length"`
2104+ PromptFilterSensitiveWords string `json:"prompt_filter_sensitive_words"`
2105+ PromptFilterCustomPatterns string `json:"prompt_filter_custom_patterns"`
2106+ PromptFilterDisabledPatterns string `json:"prompt_filter_disabled_patterns"`
20932107}
20942108
20952109type updateSettingsReq struct {
@@ -2116,6 +2130,15 @@ type updateSettingsReq struct {
21162130 ModelMapping * string `json:"model_mapping"`
21172131 ResinURL * string `json:"resin_url"`
21182132 ResinPlatformName * string `json:"resin_platform_name"`
2133+ PromptFilterEnabled * bool `json:"prompt_filter_enabled"`
2134+ PromptFilterMode * string `json:"prompt_filter_mode"`
2135+ PromptFilterThreshold * int `json:"prompt_filter_threshold"`
2136+ PromptFilterStrictThreshold * int `json:"prompt_filter_strict_threshold"`
2137+ PromptFilterLogMatches * bool `json:"prompt_filter_log_matches"`
2138+ PromptFilterMaxTextLength * int `json:"prompt_filter_max_text_length"`
2139+ PromptFilterSensitiveWords * string `json:"prompt_filter_sensitive_words"`
2140+ PromptFilterCustomPatterns * string `json:"prompt_filter_custom_patterns"`
2141+ PromptFilterDisabledPatterns * string `json:"prompt_filter_disabled_patterns"`
21192142}
21202143
21212144// GetSettings 获取当前系统设置
@@ -2133,6 +2156,7 @@ func (h *Handler) GetSettings(c *gin.Context) {
21332156 resinURL = dbSettings .ResinURL
21342157 resinPlatformName = dbSettings .ResinPlatformName
21352158 }
2159+ promptFilterCfg := h .store .GetPromptFilterConfig ()
21362160 c .JSON (http .StatusOK , settingsResponse {
21372161 MaxConcurrency : h .store .GetMaxConcurrency (),
21382162 GlobalRPM : h .rateLimiter .GetRPM (),
@@ -2162,6 +2186,15 @@ func (h *Handler) GetSettings(c *gin.Context) {
21622186 ModelMapping : h .store .GetModelMapping (),
21632187 ResinURL : resinURL ,
21642188 ResinPlatformName : resinPlatformName ,
2189+ PromptFilterEnabled : promptFilterCfg .Enabled ,
2190+ PromptFilterMode : promptFilterCfg .Mode ,
2191+ PromptFilterThreshold : promptFilterCfg .Threshold ,
2192+ PromptFilterStrictThreshold : promptFilterCfg .StrictThreshold ,
2193+ PromptFilterLogMatches : promptFilterCfg .LogMatches ,
2194+ PromptFilterMaxTextLength : promptFilterCfg .MaxTextLength ,
2195+ PromptFilterSensitiveWords : promptFilterCfg .SensitiveWords ,
2196+ PromptFilterCustomPatterns : promptfilter .MarshalCustomPatterns (promptFilterCfg .CustomPatterns ),
2197+ PromptFilterDisabledPatterns : promptfilter .MarshalDisabledPatterns (promptFilterCfg .DisabledPatterns ),
21652198 })
21662199}
21672200
@@ -2363,6 +2396,64 @@ func (h *Handler) UpdateSettings(c *gin.Context) {
23632396 log .Printf ("设置已更新: model_mapping" )
23642397 }
23652398
2399+ promptFilterCfg := h .store .GetPromptFilterConfig ()
2400+ promptFilterChanged := false
2401+ if req .PromptFilterEnabled != nil {
2402+ promptFilterCfg .Enabled = * req .PromptFilterEnabled
2403+ promptFilterChanged = true
2404+ }
2405+ if req .PromptFilterMode != nil {
2406+ promptFilterCfg .Mode = * req .PromptFilterMode
2407+ promptFilterChanged = true
2408+ }
2409+ if req .PromptFilterThreshold != nil {
2410+ promptFilterCfg .Threshold = * req .PromptFilterThreshold
2411+ promptFilterChanged = true
2412+ }
2413+ if req .PromptFilterStrictThreshold != nil {
2414+ promptFilterCfg .StrictThreshold = * req .PromptFilterStrictThreshold
2415+ promptFilterChanged = true
2416+ }
2417+ if req .PromptFilterLogMatches != nil {
2418+ promptFilterCfg .LogMatches = * req .PromptFilterLogMatches
2419+ promptFilterChanged = true
2420+ }
2421+ if req .PromptFilterMaxTextLength != nil {
2422+ promptFilterCfg .MaxTextLength = * req .PromptFilterMaxTextLength
2423+ promptFilterChanged = true
2424+ }
2425+ if req .PromptFilterSensitiveWords != nil {
2426+ promptFilterCfg .SensitiveWords = * req .PromptFilterSensitiveWords
2427+ promptFilterChanged = true
2428+ }
2429+ if req .PromptFilterCustomPatterns != nil {
2430+ patterns , err := promptfilter .ParseCustomPatterns (* req .PromptFilterCustomPatterns )
2431+ if err != nil {
2432+ writeError (c , http .StatusBadRequest , "Prompt 检查自定义规则 JSON 无效: " + err .Error ())
2433+ return
2434+ }
2435+ promptFilterCfg .CustomPatterns = patterns
2436+ promptFilterChanged = true
2437+ }
2438+ if req .PromptFilterDisabledPatterns != nil {
2439+ disabled , err := promptfilter .ParseDisabledPatterns (* req .PromptFilterDisabledPatterns )
2440+ if err != nil {
2441+ writeError (c , http .StatusBadRequest , "Prompt 检查禁用规则 JSON 无效: " + err .Error ())
2442+ return
2443+ }
2444+ promptFilterCfg .DisabledPatterns = disabled
2445+ promptFilterChanged = true
2446+ }
2447+ if promptFilterChanged {
2448+ promptFilterCfg = promptfilter .NormalizeConfig (promptFilterCfg )
2449+ if _ , err := promptfilter .NewEngine (promptFilterCfg ); err != nil {
2450+ writeError (c , http .StatusBadRequest , "Prompt 检查规则无效: " + err .Error ())
2451+ return
2452+ }
2453+ h .store .SetPromptFilterConfig (promptFilterCfg )
2454+ log .Printf ("设置已更新: prompt_filter enabled=%t mode=%s threshold=%d" , promptFilterCfg .Enabled , promptFilterCfg .Mode , promptFilterCfg .Threshold )
2455+ }
2456+
23662457 // Resin 粘性代理池配置
23672458 resinURL := ""
23682459 resinPlatformName := ""
@@ -2417,6 +2508,15 @@ func (h *Handler) UpdateSettings(c *gin.Context) {
24172508 ModelMapping : h .store .GetModelMapping (),
24182509 ResinURL : resinURL ,
24192510 ResinPlatformName : resinPlatformName ,
2511+ PromptFilterEnabled : promptFilterCfg .Enabled ,
2512+ PromptFilterMode : promptFilterCfg .Mode ,
2513+ PromptFilterThreshold : promptFilterCfg .Threshold ,
2514+ PromptFilterStrictThreshold : promptFilterCfg .StrictThreshold ,
2515+ PromptFilterLogMatches : promptFilterCfg .LogMatches ,
2516+ PromptFilterMaxTextLength : promptFilterCfg .MaxTextLength ,
2517+ PromptFilterSensitiveWords : promptFilterCfg .SensitiveWords ,
2518+ PromptFilterCustomPatterns : promptfilter .MarshalCustomPatterns (promptFilterCfg .CustomPatterns ),
2519+ PromptFilterDisabledPatterns : promptfilter .MarshalDisabledPatterns (promptFilterCfg .DisabledPatterns ),
24202520 })
24212521 if err != nil {
24222522 log .Printf ("无法持久化保存设置: %v" , err )
@@ -2465,6 +2565,15 @@ func (h *Handler) UpdateSettings(c *gin.Context) {
24652565 ModelMapping : h .store .GetModelMapping (),
24662566 ResinURL : resinURL ,
24672567 ResinPlatformName : resinPlatformName ,
2568+ PromptFilterEnabled : promptFilterCfg .Enabled ,
2569+ PromptFilterMode : promptFilterCfg .Mode ,
2570+ PromptFilterThreshold : promptFilterCfg .Threshold ,
2571+ PromptFilterStrictThreshold : promptFilterCfg .StrictThreshold ,
2572+ PromptFilterLogMatches : promptFilterCfg .LogMatches ,
2573+ PromptFilterMaxTextLength : promptFilterCfg .MaxTextLength ,
2574+ PromptFilterSensitiveWords : promptFilterCfg .SensitiveWords ,
2575+ PromptFilterCustomPatterns : promptfilter .MarshalCustomPatterns (promptFilterCfg .CustomPatterns ),
2576+ PromptFilterDisabledPatterns : promptfilter .MarshalDisabledPatterns (promptFilterCfg .DisabledPatterns ),
24682577 })
24692578}
24702579
0 commit comments