Full Go backend + Vue 3 frontend, OpenAI-compatible API, multi-provider account pools, billing/admin, Docker one-command deploy with auto HTTPS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
1.1 KiB
Go
47 lines
1.1 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func GeneratePasswordHash(password string) (string, error) {
|
|
hash, err := bcrypt.GenerateFromPassword(bcryptPrehash(password), 12)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(hash), nil
|
|
}
|
|
|
|
func VerifyPassword(password, stored string) bool {
|
|
stored = strings.TrimSpace(stored)
|
|
if stored == "" {
|
|
return false
|
|
}
|
|
|
|
if strings.HasPrefix(stored, "bcrypt$") {
|
|
hash := stored[len("bcrypt$"):]
|
|
return bcrypt.CompareHashAndPassword([]byte(hash), bcryptPrehash(password)) == nil
|
|
}
|
|
|
|
parts := strings.SplitN(stored, "$", 3)
|
|
if len(parts) != 3 || parts[0] != "sha256" {
|
|
return false
|
|
}
|
|
expected := sha256.Sum256([]byte(parts[1] + password))
|
|
expectedHex := hex.EncodeToString(expected[:])
|
|
return subtle.ConstantTimeCompare([]byte(expectedHex), []byte(parts[2])) == 1
|
|
}
|
|
|
|
func bcryptPrehash(password string) []byte {
|
|
sum := sha256.Sum256([]byte(password))
|
|
dst := make([]byte, base64.StdEncoding.EncodedLen(len(sum)))
|
|
base64.StdEncoding.Encode(dst, sum[:])
|
|
return dst
|
|
}
|