diff --git a/frontend/src/views/settings/ModelSettings.vue b/frontend/src/views/settings/ModelSettings.vue
index e735bb274..e22b293bc 100644
--- a/frontend/src/views/settings/ModelSettings.vue
+++ b/frontend/src/views/settings/ModelSettings.vue
@@ -28,7 +28,7 @@
WeKnoraCloud 凭证已失效
- {{ weKnoraCloudReinitReason || '服务重启后加密密钥已变更,已保存的凭证无法解密。' }}请重新填写 APPID 和 APPSECRET 并点击"保存并初始化"以恢复服务。
+ {{ weKnoraCloudReinitReason || '服务重启后加密密钥已变更,已保存的凭证无法解密。' }}
diff --git a/internal/application/service/model.go b/internal/application/service/model.go
index 86db67cf5..65ec1756a 100644
--- a/internal/application/service/model.go
+++ b/internal/application/service/model.go
@@ -4,7 +4,6 @@ import (
"context"
"errors"
- "github.com/Tencent/WeKnora/internal/infrastructure/crypto"
"github.com/Tencent/WeKnora/internal/logger"
"github.com/Tencent/WeKnora/internal/models/asr"
"github.com/Tencent/WeKnora/internal/models/chat"
@@ -14,6 +13,7 @@ import (
"github.com/Tencent/WeKnora/internal/models/vlm"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/types/interfaces"
+ "github.com/Tencent/WeKnora/internal/utils"
)
// ErrModelNotFound is returned when a model cannot be found in the repository
@@ -24,20 +24,17 @@ type modelService struct {
repo interfaces.ModelRepository
ollamaService *ollama.OllamaService
pooler embedding.EmbedderPooler
- cryptoSvc *crypto.CryptoService
}
// NewModelService creates a new model service instance
func NewModelService(repo interfaces.ModelRepository,
ollamaService *ollama.OllamaService,
pooler embedding.EmbedderPooler,
- cryptoSvc *crypto.CryptoService,
) interfaces.ModelService {
return &modelService{
repo: repo,
ollamaService: ollamaService,
pooler: pooler,
- cryptoSvc: cryptoSvc,
}
}
@@ -46,11 +43,12 @@ func (s *modelService) decryptAppSecret(encrypted string) string {
if encrypted == "" {
return encrypted
}
- plain, err := s.cryptoSvc.DecryptString(encrypted)
- if err != nil {
- return ""
+ if key := utils.GetAESKey(); key != nil {
+ if encrypted, err := utils.DecryptAESGCM(encrypted, key); err == nil {
+ return encrypted
+ }
}
- return plain
+ return encrypted
}
// CreateModel creates a new model in the repository
diff --git a/internal/application/service/tenant.go b/internal/application/service/tenant.go
index faed43145..0f26c1415 100644
--- a/internal/application/service/tenant.go
+++ b/internal/application/service/tenant.go
@@ -13,7 +13,6 @@ import (
"strings"
"time"
- "github.com/Tencent/WeKnora/internal/infrastructure/crypto"
"github.com/Tencent/WeKnora/internal/logger"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/types/interfaces"
@@ -34,13 +33,12 @@ type ListTenantsParams struct {
// tenantService implements the TenantService interface
type tenantService struct {
- repo interfaces.TenantRepository // Repository for tenant data operations
- cryptoSvc *crypto.CryptoService
+ repo interfaces.TenantRepository // Repository for tenant data operations
}
// NewTenantService creates a new tenant service instance
-func NewTenantService(repo interfaces.TenantRepository, cryptoSvc *crypto.CryptoService) interfaces.TenantService {
- return &tenantService{repo: repo, cryptoSvc: cryptoSvc}
+func NewTenantService(repo interfaces.TenantRepository) interfaces.TenantService {
+ return &tenantService{repo: repo}
}
// CreateTenant creates a new tenant
@@ -353,11 +351,12 @@ func (s *tenantService) GetDocreaderCredentials(ctx context.Context) *types.Docr
if appID == "" || tenant.ParserEngineConfig.DocreaderAPIKey == "" {
return nil
}
- apiKey, err := s.cryptoSvc.DecryptString(tenant.ParserEngineConfig.DocreaderAPIKey)
- if err != nil || apiKey == "" {
- return nil
+ if key := utils.GetAESKey(); key != nil {
+ if encrypted, err := utils.DecryptAESGCM(tenant.ParserEngineConfig.DocreaderAPIKey, key); err == nil {
+ return &types.DocreaderCredentials{AppID: appID, APIKey: encrypted}
+ }
}
- return &types.DocreaderCredentials{AppID: appID, APIKey: apiKey}
+ return &types.DocreaderCredentials{AppID: appID, APIKey: tenant.ParserEngineConfig.DocreaderAPIKey}
}
}
@@ -372,10 +371,12 @@ func (s *tenantService) GetDocreaderCredentials(ctx context.Context) *types.Docr
if err == nil && tenant != nil && tenant.ParserEngineConfig != nil {
appID := strings.TrimSpace(tenant.ParserEngineConfig.DocreaderAppID)
if appID != "" && tenant.ParserEngineConfig.DocreaderAPIKey != "" {
- apiKey, err := s.cryptoSvc.DecryptString(tenant.ParserEngineConfig.DocreaderAPIKey)
- if err == nil && apiKey != "" {
- return &types.DocreaderCredentials{AppID: appID, APIKey: apiKey}
+ if key := utils.GetAESKey(); key != nil {
+ if encrypted, err := utils.DecryptAESGCM(tenant.ParserEngineConfig.DocreaderAPIKey, key); err == nil {
+ return &types.DocreaderCredentials{AppID: appID, APIKey: encrypted}
+ }
}
+ return &types.DocreaderCredentials{AppID: appID, APIKey: tenant.ParserEngineConfig.DocreaderAPIKey}
}
}
diff --git a/internal/application/service/weknoracloud.go b/internal/application/service/weknoracloud.go
index f63fe45f8..ae7fc394a 100644
--- a/internal/application/service/weknoracloud.go
+++ b/internal/application/service/weknoracloud.go
@@ -7,29 +7,28 @@ import (
"strings"
"time"
- "github.com/Tencent/WeKnora/internal/infrastructure/crypto"
"github.com/Tencent/WeKnora/internal/models/provider"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/types/interfaces"
+ "github.com/Tencent/WeKnora/internal/utils"
"github.com/google/uuid"
)
+const WeKnoraCloudAPI = "https://weknora.woa.com/platform/openapi"
+
type weKnoraCloudService struct {
repo interfaces.ModelRepository
tenantRepo interfaces.TenantRepository
- cryptoSvc *crypto.CryptoService
}
// NewWeKnoraCloudService 构造 WeKnoraCloudService
func NewWeKnoraCloudService(
repo interfaces.ModelRepository,
tenantRepo interfaces.TenantRepository,
- cryptoSvc *crypto.CryptoService,
) interfaces.WeKnoraCloudService {
return &weKnoraCloudService{
repo: repo,
tenantRepo: tenantRepo,
- cryptoSvc: cryptoSvc,
}
}
@@ -99,9 +98,11 @@ func (s *weKnoraCloudService) prepareInitialize(ctx context.Context, appID, appS
}
// Step 2: Encrypt appSecret
- encryptedSecret, err := s.cryptoSvc.EncryptString(appSecret)
- if err != nil {
- return nil, fmt.Errorf("encrypt AppSecret failed: %w", err)
+ encryptedSecret := appSecret
+ if key := utils.GetAESKey(); key != nil {
+ if encrypted, err := utils.EncryptAESGCM(appSecret, key); err == nil {
+ encryptedSecret = encrypted
+ }
}
// Step 3: Get tenantID and take snapshot
@@ -296,18 +297,19 @@ func (s *weKnoraCloudService) CheckStatus(ctx context.Context) (*types.WeKnoraCl
return &types.WeKnoraCloudStatusResult{
HasModels: true,
NeedsReinit: true,
- Reason: "WeKnoraCloud 凭证为空,请重新填写 APPID 和 API Key",
+ Reason: fmt.Sprintf("WeKnoraCloud 凭证为空,请重新填写 APPID 和 API Key, 请前往:%s", WeKnoraCloudAPI),
}, nil
}
// Try to decrypt the API key
- _, decErr := s.cryptoSvc.DecryptString(tenant.ParserEngineConfig.DocreaderAPIKey)
- if decErr != nil {
- return &types.WeKnoraCloudStatusResult{
- HasModels: true,
- NeedsReinit: true,
- Reason: "WeKnoraCloud 凭证解密失败(服务重启后加密密钥已变更),请重新填写 APPID 和 API Key",
- }, nil
+ if key := utils.GetAESKey(); key != nil {
+ if _, err := utils.DecryptAESGCM(tenant.ParserEngineConfig.DocreaderAPIKey, key); err != nil {
+ return &types.WeKnoraCloudStatusResult{
+ HasModels: true,
+ NeedsReinit: true,
+ Reason: "WeKnoraCloud 凭证解密失败(服务重启后加密密钥已变更),请重新填写 APPID 和 API Key",
+ }, nil
+ }
}
return &types.WeKnoraCloudStatusResult{HasModels: true, NeedsReinit: false}, nil
diff --git a/internal/container/container.go b/internal/container/container.go
index 1edac5a2a..858800592 100644
--- a/internal/container/container.go
+++ b/internal/container/container.go
@@ -6,7 +6,6 @@ package container
import (
"context"
"database/sql"
- "encoding/base64"
"encoding/json"
"fmt"
"net/url"
@@ -64,7 +63,6 @@ import (
"github.com/Tencent/WeKnora/internal/im/telegram"
"github.com/Tencent/WeKnora/internal/im/wechat"
"github.com/Tencent/WeKnora/internal/im/wecom"
- "github.com/Tencent/WeKnora/internal/infrastructure/crypto"
"github.com/Tencent/WeKnora/internal/infrastructure/docparser"
infra_web_search "github.com/Tencent/WeKnora/internal/infrastructure/web_search"
"github.com/Tencent/WeKnora/internal/logger"
@@ -100,7 +98,6 @@ func BuildContainer(container *dig.Container) *dig.Container {
// Core infrastructure configuration
logger.Debugf(ctx, "[Container] Registering core infrastructure...")
must(container.Provide(config.LoadConfig))
- must(container.Provide(initCryptoService))
must(container.Provide(initTracer))
must(container.Provide(initDatabase))
must(container.Provide(initFileService))
@@ -311,92 +308,6 @@ func initTracer() (*tracing.Tracer, error) {
return tracing.InitTracer()
}
-// cryptoStateFile 是持久化 crypto 状态的文件路径,存储在 data-files volume 中
-// 保证重启后能恢复相同的 masterKey 和 salt,避免已加密数据无法解密
-const cryptoStateFile = "/data/files/.crypto_state.json"
-
-// cryptoState 持久化存储的 crypto 配置
-type cryptoState struct {
- MasterKey string `json:"master_key"`
- Salt string `json:"salt"` // base64 编码
-}
-
-func initCryptoService() (*crypto.CryptoService, error) {
- cfg := crypto.LoadConfigFromEnv()
-
- // 如果环境变量同时提供了 MasterKey 和 Salt,直接使用(优先级最高)
- if cfg.MasterKey != "" && cfg.Salt != "" {
- return crypto.NewCryptoServiceFromConfig(cfg)
- }
-
- // 尝试从持久化文件恢复状态
- if state, err := loadCryptoState(cryptoStateFile); err == nil {
- // 文件读取成功,环境变量中未覆盖的字段从文件补充
- if cfg.MasterKey == "" {
- cfg.MasterKey = state.MasterKey
- }
- if cfg.Salt == "" {
- cfg.Salt = state.Salt
- }
- return crypto.NewCryptoServiceFromConfig(cfg)
- }
-
- // 文件不存在或读取失败,使用/生成配置后写入文件
- if cfg.MasterKey == "" {
- cfg.MasterKey = "weknora-default-key"
- }
-
- // 调用 NewCryptoServiceFromConfig 会在 cfg.Salt 为空时自动生成随机 salt
- svc, err := crypto.NewCryptoServiceFromConfig(cfg)
- if err != nil {
- return nil, err
- }
-
- // 将实际使用的 masterKey 和 salt 持久化,供重启恢复
- saltB64 := base64.StdEncoding.EncodeToString(svc.GetSalt())
- state := &cryptoState{
- MasterKey: cfg.MasterKey,
- Salt: saltB64,
- }
- if saveErr := saveCryptoState(cryptoStateFile, state); saveErr != nil {
- // 持久化失败仅记录警告,不阻止服务启动
- logger.Warnf(context.Background(), "[CryptoService] 无法持久化 crypto 状态到 %s: %v (重启后已加密数据将无法解密)", cryptoStateFile, saveErr)
- } else {
- logger.Infof(context.Background(), "[CryptoService] crypto 状态已持久化到 %s", cryptoStateFile)
- }
-
- return svc, nil
-}
-
-// loadCryptoState 从文件加载 crypto 状态
-func loadCryptoState(path string) (*cryptoState, error) {
- data, err := os.ReadFile(path)
- if err != nil {
- return nil, err
- }
- var state cryptoState
- if err := json.Unmarshal(data, &state); err != nil {
- return nil, fmt.Errorf("parse crypto state: %w", err)
- }
- if state.MasterKey == "" || state.Salt == "" {
- return nil, fmt.Errorf("invalid crypto state: missing fields")
- }
- return &state, nil
-}
-
-// saveCryptoState 将 crypto 状态写入文件
-func saveCryptoState(path string, state *cryptoState) error {
- if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
- return fmt.Errorf("mkdir: %w", err)
- }
- data, err := json.Marshal(state)
- if err != nil {
- return fmt.Errorf("marshal: %w", err)
- }
- // 权限 0o600:仅 owner 可读写
- return os.WriteFile(path, data, 0o600)
-}
-
func initRedisClient() (*redis.Client, error) {
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
diff --git a/internal/infrastructure/crypto/config.go b/internal/infrastructure/crypto/config.go
deleted file mode 100644
index f9db8c7eb..000000000
--- a/internal/infrastructure/crypto/config.go
+++ /dev/null
@@ -1,224 +0,0 @@
-package crypto
-
-import (
- "encoding/base64"
- "fmt"
- "os"
- "strconv"
-)
-
-// Config 加密服务配置结构
-type Config struct {
- // MasterKey 主密钥,用于派生加密密钥
- // 可以从环境变量或配置文件中读取
- MasterKey string `json:"master_key" yaml:"master_key" env:"CRYPTO_MASTER_KEY"`
-
- // Salt 盐值,用于密钥派生
- // 如果为空,将自动生成随机盐值
- Salt string `json:"salt" yaml:"salt" env:"CRYPTO_SALT"`
-
- // SaltLength 盐值长度(当Salt为空时使用)
- SaltLength int `json:"salt_length" yaml:"salt_length" env:"CRYPTO_SALT_LENGTH" default:"16"`
-
- // KeyDerivationIterations PBKDF2迭代次数
- KeyDerivationIterations int `json:"key_derivation_iterations" yaml:"key_derivation_iterations" env:"CRYPTO_ITERATIONS" default:"10000"`
-
- // KeyLength 派生密钥长度
- KeyLength int `json:"key_length" yaml:"key_length" env:"CRYPTO_KEY_LENGTH" default:"32"`
-}
-
-// DefaultConfig 返回默认配置
-func DefaultConfig() *Config {
- return &Config{
- MasterKey: "",
- Salt: "",
- SaltLength: 16,
- KeyDerivationIterations: 10000,
- KeyLength: 32,
- }
-}
-
-// LoadConfigFromEnv 从环境变量加载配置
-func LoadConfigFromEnv() *Config {
- config := DefaultConfig()
-
- // 从环境变量读取主密钥
- if masterKey := os.Getenv("CRYPTO_MASTER_KEY"); masterKey != "" {
- config.MasterKey = masterKey
- }
-
- // 从环境变量读取盐值
- if salt := os.Getenv("CRYPTO_SALT"); salt != "" {
- config.Salt = salt
- }
-
- // 从环境变量读取盐值长度
- if saltLengthStr := os.Getenv("CRYPTO_SALT_LENGTH"); saltLengthStr != "" {
- if saltLength, err := strconv.Atoi(saltLengthStr); err == nil && saltLength >= 8 {
- config.SaltLength = saltLength
- }
- }
-
- // 从环境变量读取迭代次数
- if iterationsStr := os.Getenv("CRYPTO_ITERATIONS"); iterationsStr != "" {
- if iterations, err := strconv.Atoi(iterationsStr); err == nil && iterations > 0 {
- config.KeyDerivationIterations = iterations
- }
- }
-
- // 从环境变量读取密钥长度
- if keyLengthStr := os.Getenv("CRYPTO_KEY_LENGTH"); keyLengthStr != "" {
- if keyLength, err := strconv.Atoi(keyLengthStr); err == nil && keyLength >= 16 {
- config.KeyLength = keyLength
- }
- }
-
- return config
-}
-
-// Validate 验证配置是否有效
-func (c *Config) Validate() error {
- if c.MasterKey == "" {
- return fmt.Errorf("master key cannot be empty")
- }
-
- if c.SaltLength < 8 {
- return fmt.Errorf("salt length must be at least 8 bytes")
- }
-
- if c.KeyDerivationIterations < 1000 {
- return fmt.Errorf("key derivation iterations must be at least 1000")
- }
-
- if c.KeyLength < 16 {
- return fmt.Errorf("key length must be at least 16 bytes")
- }
-
- return nil
-}
-
-// GetSaltBytes 获取盐值的字节数组形式
-func (c *Config) GetSaltBytes() ([]byte, error) {
- if c.Salt != "" {
- // 如果配置了盐值,解码Base64格式
- saltBytes, err := base64.StdEncoding.DecodeString(c.Salt)
- if err != nil {
- return nil, fmt.Errorf("failed to decode salt from base64: %w", err)
- }
- return saltBytes, nil
- }
-
- // 如果没有配置盐值,生成随机盐值
- salt, err := GenerateRandomSalt(c.SaltLength)
- if err != nil {
- return nil, fmt.Errorf("failed to generate random salt: %w", err)
- }
-
- return salt, nil
-}
-
-// NewCryptoServiceFromConfig 从配置创建加密服务
-func NewCryptoServiceFromConfig(config *Config) (*CryptoService, error) {
- if err := config.Validate(); err != nil {
- return nil, fmt.Errorf("invalid config: %w", err)
- }
-
- saltBytes, err := config.GetSaltBytes()
- if err != nil {
- return nil, fmt.Errorf("failed to get salt bytes: %w", err)
- }
-
- return NewCryptoService(config.MasterKey, saltBytes)
-}
-
-// ExampleConfigUsage 展示配置使用示例
-func ExampleConfigUsage() {
- fmt.Println("=== 配置使用示例 ===")
-
- // 方法1: 手动创建配置
- manualConfig := &Config{
- MasterKey: "my-secret-master-key",
- Salt: "", // 自动生成盐值
- SaltLength: 16,
- KeyDerivationIterations: 10000,
- KeyLength: 32,
- }
-
- cryptoService1, err := NewCryptoServiceFromConfig(manualConfig)
- if err != nil {
- fmt.Printf("手动配置创建失败: %v\n", err)
- } else {
- fmt.Println("手动配置创建成功")
- _ = cryptoService1
- }
-
- // 方法2: 从环境变量加载配置
- // 首先设置环境变量
- os.Setenv("CRYPTO_MASTER_KEY", "env-master-key")
- os.Setenv("CRYPTO_SALT", base64.StdEncoding.EncodeToString([]byte("env-salt-123456")))
- os.Setenv("CRYPTO_SALT_LENGTH", "16")
- os.Setenv("CRYPTO_ITERATIONS", "10000")
- os.Setenv("CRYPTO_KEY_LENGTH", "32")
-
- envConfig := LoadConfigFromEnv()
- cryptoService2, err := NewCryptoServiceFromConfig(envConfig)
- if err != nil {
- fmt.Printf("环境变量配置创建失败: %v\n", err)
- } else {
- fmt.Println("环境变量配置创建成功")
- _ = cryptoService2
- }
-
- // 清理环境变量
- os.Unsetenv("CRYPTO_MASTER_KEY")
- os.Unsetenv("CRYPTO_SALT")
- os.Unsetenv("CRYPTO_SALT_LENGTH")
- os.Unsetenv("CRYPTO_ITERATIONS")
- os.Unsetenv("CRYPTO_KEY_LENGTH")
-}
-
-// GenerateConfigExample 生成配置示例
-func GenerateConfigExample() {
- fmt.Println("\n=== 配置生成示例 ===")
-
- // 生成随机主密钥
- masterKey, err := GenerateRandomKey(32)
- if err != nil {
- fmt.Printf("生成主密钥失败: %v\n", err)
- return
- }
-
- // 生成随机盐值
- salt, err := GenerateRandomSalt(16)
- if err != nil {
- fmt.Printf("生成盐值失败: %v\n", err)
- return
- }
-
- saltBase64 := base64.StdEncoding.EncodeToString(salt)
-
- fmt.Println("生成的配置示例:")
- fmt.Printf("CRYPTO_MASTER_KEY=%s\n", masterKey)
- fmt.Printf("CRYPTO_SALT=%s\n", saltBase64)
- fmt.Printf("CRYPTO_SALT_LENGTH=16\n")
- fmt.Printf("CRYPTO_ITERATIONS=10000\n")
- fmt.Printf("CRYPTO_KEY_LENGTH=32\n")
-
- fmt.Println("\nYAML格式配置示例:")
- fmt.Printf(`master_key: %s
-salt: %s
-salt_length: 16
-key_derivation_iterations: 10000
-key_length: 32
-`, masterKey, saltBase64)
-
- fmt.Println("\nJSON格式配置示例:")
- fmt.Printf(`{
- "master_key": "%s",
- "salt": "%s",
- "salt_length": 16,
- "key_derivation_iterations": 10000,
- "key_length": 32
-}
-`, masterKey, saltBase64)
-}
diff --git a/internal/infrastructure/crypto/crypto.go b/internal/infrastructure/crypto/crypto.go
deleted file mode 100644
index d2a474658..000000000
--- a/internal/infrastructure/crypto/crypto.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package crypto
-
-import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "crypto/sha256"
- "encoding/base64"
- "errors"
- "fmt"
-
- "golang.org/x/crypto/pbkdf2"
-)
-
-// CryptoService 提供密钥加密解密服务
-type CryptoService struct {
- masterKey []byte
- salt []byte
-}
-
-// NewCryptoService 创建新的加密服务实例
-// masterKey: 主密钥,用于派生加密密钥
-// salt: 盐值,用于密钥派生,如果为空则生成随机盐
-func NewCryptoService(masterKey string, salt []byte) (*CryptoService, error) {
- if masterKey == "" {
- return nil, errors.New("master key cannot be empty")
- }
-
- // 如果未提供盐值,生成随机盐
- if salt == nil || len(salt) == 0 {
- salt = make([]byte, 16)
- if _, err := rand.Read(salt); err != nil {
- return nil, fmt.Errorf("failed to generate salt: %w", err)
- }
- }
-
- return &CryptoService{
- masterKey: []byte(masterKey),
- salt: salt,
- }, nil
-}
-
-// deriveKey 使用PBKDF2派生加密密钥
-func (cs *CryptoService) deriveKey() []byte {
- return pbkdf2.Key(cs.masterKey, cs.salt, 10000, 32, sha256.New)
-}
-
-// Encrypt 加密数据
-func (cs *CryptoService) Encrypt(plaintext []byte) (string, error) {
- if len(plaintext) == 0 {
- return "", errors.New("plaintext cannot be empty")
- }
-
- key := cs.deriveKey()
-
- // 创建AES块密码
- block, err := aes.NewCipher(key)
- if err != nil {
- return "", fmt.Errorf("failed to create cipher: %w", err)
- }
-
- // 生成随机IV
- iv := make([]byte, aes.BlockSize)
- if _, err := rand.Read(iv); err != nil {
- return "", fmt.Errorf("failed to generate IV: %w", err)
- }
-
- // 使用CTR模式加密
- stream := cipher.NewCTR(block, iv)
- ciphertext := make([]byte, len(plaintext))
- stream.XORKeyStream(ciphertext, plaintext)
-
- // 组合IV和密文
- result := make([]byte, len(iv)+len(ciphertext))
- copy(result[:aes.BlockSize], iv)
- copy(result[aes.BlockSize:], ciphertext)
-
- // 返回Base64编码的结果
- return base64.StdEncoding.EncodeToString(result), nil
-}
-
-// Decrypt 解密数据
-func (cs *CryptoService) Decrypt(encryptedData string) ([]byte, error) {
- if encryptedData == "" {
- return nil, errors.New("encrypted data cannot be empty")
- }
-
- // 解码Base64数据
- data, err := base64.StdEncoding.DecodeString(encryptedData)
- if err != nil {
- return nil, fmt.Errorf("failed to decode base64: %w", err)
- }
-
- if len(data) < aes.BlockSize {
- return nil, errors.New("encrypted data too short")
- }
-
- key := cs.deriveKey()
-
- // 创建AES块密码
- block, err := aes.NewCipher(key)
- if err != nil {
- return nil, fmt.Errorf("failed to create cipher: %w", err)
- }
-
- // 提取IV和密文
- iv := data[:aes.BlockSize]
- ciphertext := data[aes.BlockSize:]
-
- // 使用CTR模式解密
- stream := cipher.NewCTR(block, iv)
- plaintext := make([]byte, len(ciphertext))
- stream.XORKeyStream(plaintext, ciphertext)
-
- return plaintext, nil
-}
-
-// EncryptString 加密字符串
-func (cs *CryptoService) EncryptString(plaintext string) (string, error) {
- return cs.Encrypt([]byte(plaintext))
-}
-
-// DecryptString 解密字符串
-func (cs *CryptoService) DecryptString(encryptedData string) (string, error) {
- plaintext, err := cs.Decrypt(encryptedData)
- if err != nil {
- return "", err
- }
- return string(plaintext), nil
-}
-
-// GetSalt 获取当前使用的盐值
-func (cs *CryptoService) GetSalt() []byte {
- return cs.salt
-}
-
-// GenerateRandomKey 生成随机密钥
-func GenerateRandomKey(length int) (string, error) {
- if length < 16 {
- return "", errors.New("key length must be at least 16 bytes")
- }
-
- key := make([]byte, length)
- if _, err := rand.Read(key); err != nil {
- return "", fmt.Errorf("failed to generate random key: %w", err)
- }
-
- return base64.StdEncoding.EncodeToString(key), nil
-}
-
-// GenerateRandomSalt 生成随机盐值
-func GenerateRandomSalt(length int) ([]byte, error) {
- if length < 8 {
- return nil, errors.New("salt length must be at least 8 bytes")
- }
-
- salt := make([]byte, length)
- if _, err := rand.Read(salt); err != nil {
- return nil, fmt.Errorf("failed to generate random salt: %w", err)
- }
-
- return salt, nil
-}
diff --git a/internal/models/embedding/weknoracloud.go b/internal/models/embedding/weknoracloud.go
index 9dec9682e..158d7af60 100644
--- a/internal/models/embedding/weknoracloud.go
+++ b/internal/models/embedding/weknoracloud.go
@@ -54,8 +54,9 @@ func NewWeKnoraCloudEmbedder(config Config) (*WeKnoraCloudEmbedder, error) {
}
type weKnoraCloudEmbedRequest struct {
- Model string `json:"model"`
- Input []string `json:"input"`
+ Model string `json:"model"`
+ Input []string `json:"input"`
+ TruncatePromptTokens int `json:"truncate_prompt_tokens,omitempty"`
}
type weKnoraCloudEmbedResponse struct {
@@ -77,7 +78,7 @@ func (e *WeKnoraCloudEmbedder) Embed(ctx context.Context, text string) ([]float3
}
func (e *WeKnoraCloudEmbedder) BatchEmbed(ctx context.Context, texts []string) ([][]float32, error) {
- reqBody := weKnoraCloudEmbedRequest{Model: e.effectiveModelName(), Input: texts}
+ reqBody := weKnoraCloudEmbedRequest{Model: e.effectiveModelName(), Input: texts, TruncatePromptTokens: 512}
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("weknoracloud embedder: marshal: %w", err)