package handlers import ( "crypto/rand" "crypto/sha256" "crypto/subtle" "encoding/hex" "fmt" "net/http" "strings" "github.com/gin-gonic/gin" "sproutgate-backend/internal/models" ) func bearerToken(header string) string { if header == "" { return "" } if strings.HasPrefix(strings.ToLower(header), "bearer ") { return strings.TrimSpace(header[7:]) } return "" } func adminTokenFromRequest(c *gin.Context) string { if token := strings.TrimSpace(c.Query("token")); token != "" { return token } if token := strings.TrimSpace(c.GetHeader("X-Admin-Token")); token != "" { return token } authHeader := strings.TrimSpace(c.GetHeader("Authorization")) return bearerToken(authHeader) } func generateVerificationCode() (string, error) { randomBytes := make([]byte, 3) if _, err := rand.Read(randomBytes); err != nil { return "", err } number := int(randomBytes[0])<<16 | int(randomBytes[1])<<8 | int(randomBytes[2]) return fmt.Sprintf("%06d", number%1000000), nil } func hashCode(code string) string { sum := sha256.Sum256([]byte(code)) return hex.EncodeToString(sum[:]) } func verifyCode(code string, hash string) bool { return subtle.ConstantTimeCompare([]byte(hashCode(code)), []byte(hash)) == 1 } func writeBanJSON(c *gin.Context, reason string) { h := gin.H{"error": "account is banned"} if r := strings.TrimSpace(reason); r != "" { h["banReason"] = r } c.JSON(http.StatusForbidden, h) } func abortIfUserBanned(c *gin.Context, u models.UserRecord) bool { if !u.Banned { return false } writeBanJSON(c, u.BanReason) return true }