mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
2ac48e20a4
## 🎯 簡化方案(社區友好) ### 移除雲端 KMS - ❌ 刪除 crypto/aliyun_kms.go - ❌ 不包含 GCP KMS - ✅ 僅保留本地 AES-256-GCM 加密 ### 更新 SQLite 驅動 - ✅ modernc.org/sqlite(純 Go,無 CGO) - ✅ 與上游保持一致 ## 📦 保留核心功能 ✅ crypto/encryption.go - RSA + AES 加密 ✅ crypto/secure_storage.go - 數據庫加密層 ✅ api/crypto_handler.go - API 端點 ✅ web/src/lib/crypto.ts - 前端加密 ✅ scripts/migrate_encryption.go - 數據遷移 ## 🚀 部署方式 ```bash # 僅需一個環境變量 export NOFX_MASTER_KEY=$(openssl rand -base64 32) go run main.go ``` ## ✅ 優點 - ✅ 零雲服務依賴 - ✅ 簡單易部署 - ✅ 適合社區用戶 - ✅ 保持核心安全功能
201 lines
4.7 KiB
Go
201 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"nofx/crypto"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func main() {
|
|
log.Println("🔄 開始遷移數據庫到加密格式...")
|
|
|
|
// 1. 檢查數據庫檔案
|
|
dbPath := "config.db"
|
|
if len(os.Args) > 1 {
|
|
dbPath = os.Args[1]
|
|
}
|
|
|
|
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
|
|
log.Fatalf("❌ 數據庫檔案不存在: %s", dbPath)
|
|
}
|
|
|
|
// 2. 備份數據庫
|
|
backupPath := fmt.Sprintf("%s.pre_encryption_backup", dbPath)
|
|
log.Printf("📦 備份數據庫到: %s", backupPath)
|
|
|
|
input, err := os.ReadFile(dbPath)
|
|
if err != nil {
|
|
log.Fatalf("❌ 讀取數據庫失敗: %v", err)
|
|
}
|
|
|
|
if err := os.WriteFile(backupPath, input, 0600); err != nil {
|
|
log.Fatalf("❌ 備份失敗: %v", err)
|
|
}
|
|
|
|
// 3. 打開數據庫
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
log.Fatalf("❌ 打開數據庫失敗: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// 4. 初始化加密管理器
|
|
em, err := crypto.GetEncryptionManager()
|
|
if err != nil {
|
|
log.Fatalf("❌ 初始化加密管理器失敗: %v", err)
|
|
}
|
|
|
|
// 5. 遷移交易所配置
|
|
if err := migrateExchanges(db, em); err != nil {
|
|
log.Fatalf("❌ 遷移交易所配置失敗: %v", err)
|
|
}
|
|
|
|
// 6. 遷移 AI 模型配置
|
|
if err := migrateAIModels(db, em); err != nil {
|
|
log.Fatalf("❌ 遷移 AI 模型配置失敗: %v", err)
|
|
}
|
|
|
|
log.Println("✅ 數據遷移完成!")
|
|
log.Printf("📝 原始數據備份位於: %s", backupPath)
|
|
log.Println("⚠️ 請驗證系統功能正常後,手動刪除備份檔案")
|
|
}
|
|
|
|
// migrateExchanges 遷移交易所配置
|
|
func migrateExchanges(db *sql.DB, em *crypto.EncryptionManager) error {
|
|
log.Println("🔄 遷移交易所配置...")
|
|
|
|
// 查詢所有未加密的記錄(假設加密數據都包含 '==' Base64 特徵)
|
|
rows, err := db.Query(`
|
|
SELECT user_id, id, api_key, secret_key,
|
|
COALESCE(hyperliquid_private_key, ''),
|
|
COALESCE(aster_private_key, '')
|
|
FROM exchanges
|
|
WHERE (api_key != '' AND api_key NOT LIKE '%==%')
|
|
OR (secret_key != '' AND secret_key NOT LIKE '%==%')
|
|
`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
count := 0
|
|
for rows.Next() {
|
|
var userID, exchangeID, apiKey, secretKey, hlPrivateKey, asterPrivateKey string
|
|
if err := rows.Scan(&userID, &exchangeID, &apiKey, &secretKey, &hlPrivateKey, &asterPrivateKey); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 加密每個字段
|
|
encAPIKey, err := em.EncryptForDatabase(apiKey)
|
|
if err != nil {
|
|
return fmt.Errorf("加密 API Key 失敗: %w", err)
|
|
}
|
|
|
|
encSecretKey, err := em.EncryptForDatabase(secretKey)
|
|
if err != nil {
|
|
return fmt.Errorf("加密 Secret Key 失敗: %w", err)
|
|
}
|
|
|
|
encHLPrivateKey := ""
|
|
if hlPrivateKey != "" {
|
|
encHLPrivateKey, err = em.EncryptForDatabase(hlPrivateKey)
|
|
if err != nil {
|
|
return fmt.Errorf("加密 Hyperliquid Private Key 失敗: %w", err)
|
|
}
|
|
}
|
|
|
|
encAsterPrivateKey := ""
|
|
if asterPrivateKey != "" {
|
|
encAsterPrivateKey, err = em.EncryptForDatabase(asterPrivateKey)
|
|
if err != nil {
|
|
return fmt.Errorf("加密 Aster Private Key 失敗: %w", err)
|
|
}
|
|
}
|
|
|
|
// 更新數據庫
|
|
_, err = tx.Exec(`
|
|
UPDATE exchanges
|
|
SET api_key = ?, secret_key = ?,
|
|
hyperliquid_private_key = ?, aster_private_key = ?
|
|
WHERE user_id = ? AND id = ?
|
|
`, encAPIKey, encSecretKey, encHLPrivateKey, encAsterPrivateKey, userID, exchangeID)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("更新數據庫失敗: %w", err)
|
|
}
|
|
|
|
log.Printf(" ✓ 已加密: [%s] %s", userID, exchangeID)
|
|
count++
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("✅ 已遷移 %d 個交易所配置", count)
|
|
return nil
|
|
}
|
|
|
|
// migrateAIModels 遷移 AI 模型配置
|
|
func migrateAIModels(db *sql.DB, em *crypto.EncryptionManager) error {
|
|
log.Println("🔄 遷移 AI 模型配置...")
|
|
|
|
rows, err := db.Query(`
|
|
SELECT user_id, id, api_key
|
|
FROM ai_models
|
|
WHERE api_key != '' AND api_key NOT LIKE '%==%'
|
|
`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
count := 0
|
|
for rows.Next() {
|
|
var userID, modelID, apiKey string
|
|
if err := rows.Scan(&userID, &modelID, &apiKey); err != nil {
|
|
return err
|
|
}
|
|
|
|
encAPIKey, err := em.EncryptForDatabase(apiKey)
|
|
if err != nil {
|
|
return fmt.Errorf("加密 API Key 失敗: %w", err)
|
|
}
|
|
|
|
_, err = tx.Exec(`
|
|
UPDATE ai_models SET api_key = ? WHERE user_id = ? AND id = ?
|
|
`, encAPIKey, userID, modelID)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("更新數據庫失敗: %w", err)
|
|
}
|
|
|
|
log.Printf(" ✓ 已加密: [%s] %s", userID, modelID)
|
|
count++
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("✅ 已遷移 %d 個 AI 模型配置", count)
|
|
return nil
|
|
}
|