使用了AI重构项目,并完善了一部分后台问题
This commit is contained in:
@@ -1,15 +1,25 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DataDir string
|
||||
DatabasePath string
|
||||
UploadDir string
|
||||
JWTSecret string
|
||||
DataDir string
|
||||
DatabasePath string
|
||||
UploadDir string
|
||||
JWTSecret string
|
||||
EncryptionKey []byte
|
||||
}
|
||||
|
||||
func New(dataDir string) *Config {
|
||||
@@ -26,10 +36,71 @@ func New(dataDir string) *Config {
|
||||
jwtSecret = "your-secret-key-change-in-production"
|
||||
}
|
||||
|
||||
encryptionSource := os.Getenv("CONFIG_ENCRYPTION_KEY")
|
||||
if encryptionSource == "" {
|
||||
// Keep existing installations decryptable while allowing production deployments
|
||||
// to use a dedicated key that is independent from JWT signing.
|
||||
encryptionSource = jwtSecret
|
||||
}
|
||||
encryptionKey := sha256.Sum256([]byte(encryptionSource))
|
||||
|
||||
return &Config{
|
||||
DataDir: dataDir,
|
||||
DatabasePath: filepath.Join(dataDir, "home.db"),
|
||||
UploadDir: uploadDir,
|
||||
JWTSecret: jwtSecret,
|
||||
DataDir: dataDir,
|
||||
DatabasePath: filepath.Join(dataDir, "home.db"),
|
||||
UploadDir: uploadDir,
|
||||
JWTSecret: jwtSecret,
|
||||
EncryptionKey: encryptionKey[:],
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptSecret encrypts a value for storage in the application database.
|
||||
func (c *Config) EncryptSecret(value string) (string, error) {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return "", nil
|
||||
}
|
||||
block, err := aes.NewCipher(c.EncryptionKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", err
|
||||
}
|
||||
ciphertext := gcm.Seal(nil, nonce, []byte(value), nil)
|
||||
encoded := base64.RawStdEncoding.EncodeToString(append(nonce, ciphertext...))
|
||||
return "v1:" + encoded, nil
|
||||
}
|
||||
|
||||
// DecryptSecret decrypts a value previously returned by EncryptSecret.
|
||||
func (c *Config) DecryptSecret(value string) (string, error) {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return "", nil
|
||||
}
|
||||
if !strings.HasPrefix(value, "v1:") {
|
||||
return "", errors.New("unsupported encrypted secret format")
|
||||
}
|
||||
raw, err := base64.RawStdEncoding.DecodeString(strings.TrimPrefix(value, "v1:"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("decode encrypted secret: %w", err)
|
||||
}
|
||||
block, err := aes.NewCipher(c.EncryptionKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(raw) < gcm.NonceSize() {
|
||||
return "", errors.New("encrypted secret is too short")
|
||||
}
|
||||
plaintext, err := gcm.Open(nil, raw[:gcm.NonceSize()], raw[gcm.NonceSize():], nil)
|
||||
if err != nil {
|
||||
return "", errors.New("encrypted secret authentication failed")
|
||||
}
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user