mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 14:26:36 +08:00
1e19d8114a
- Split shared monolithic consts into bot, push, and errs with typed sentinel errors - Restructure model layer into distinct bot and push subdomains - Refactor DAO layer to enforce single-owner principle and remove cross-table raw SQL queries - Decompose 1150+ line service/push.go into push_channel, push_event, push_trigger, push_worker, and push_template - Clean up controller layer with generic request handlers and parameter validation in controller/base.go - Streamline plugin.go to core Cordis lifecycle orchestration and remove re-export bloat - Verify all unit tests, race tests, Cordis architecture rules, and Swagger generation pass cleanly
114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package service
|
|
|
|
import (
|
|
"Wavelet/pkg/util"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
credentialSecretMu sync.RWMutex
|
|
credentialSecret string
|
|
)
|
|
|
|
// SetCredentialSecret sets the secret used to derive CredentialKey.
|
|
func SetCredentialSecret(secret string) {
|
|
credentialSecretMu.Lock()
|
|
defer credentialSecretMu.Unlock()
|
|
credentialSecret = secret
|
|
}
|
|
|
|
// CredentialKey is AES-256 hex derived from the session secret.
|
|
func CredentialKey() string {
|
|
credentialSecretMu.RLock()
|
|
secret := credentialSecret
|
|
credentialSecretMu.RUnlock()
|
|
sum := sha256.Sum256([]byte(secret))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
// EncryptCredentials encrypts a credential map as JSON ciphertext.
|
|
func EncryptCredentials(creds map[string]string) (string, error) {
|
|
if creds == nil {
|
|
creds = map[string]string{}
|
|
}
|
|
raw, err := json.Marshal(creds)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return util.Encrypt(CredentialKey(), string(raw))
|
|
}
|
|
|
|
// DecryptCredentials decrypts a credential map from ciphertext.
|
|
func DecryptCredentials(ciphertext string) (map[string]string, error) {
|
|
if ciphertext == "" {
|
|
return map[string]string{}, nil
|
|
}
|
|
plain, err := util.Decrypt(CredentialKey(), ciphertext)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out map[string]string
|
|
if err := json.Unmarshal([]byte(plain), &out); err != nil {
|
|
return nil, err
|
|
}
|
|
if out == nil {
|
|
out = map[string]string{}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// ParseExtra decodes optional extra JSON into a string map.
|
|
func ParseExtra(raw string) map[string]string {
|
|
if raw == "" {
|
|
return map[string]string{}
|
|
}
|
|
var out map[string]string
|
|
if err := json.Unmarshal([]byte(raw), &out); err != nil || out == nil {
|
|
return map[string]string{}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// EncodeExtra encodes extra fields as JSON string.
|
|
func EncodeExtra(extra map[string]string) string {
|
|
if extra == nil {
|
|
return ""
|
|
}
|
|
raw, err := json.Marshal(extra)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(raw)
|
|
}
|
|
|
|
// MaskCredentials hides secret-bearing credential entries.
|
|
func MaskCredentials(_ string, in map[string]string) map[string]string {
|
|
out := make(map[string]string, len(in))
|
|
for k, v := range in {
|
|
if k == "token" || k == "client_secret" || k == "app_secret" || k == "bot_token" {
|
|
out[k] = MaskSecret(v)
|
|
} else {
|
|
out[k] = v
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
const minMaskSecretLength = 8
|
|
|
|
// MaskSecret keeps only a short visible prefix and suffix of a secret.
|
|
func MaskSecret(s string) string {
|
|
s = strings.TrimSpace(s)
|
|
if len(s) <= minMaskSecretLength {
|
|
return "******"
|
|
}
|
|
return s[:4] + "..." + s[len(s)-4:]
|
|
}
|